automating zerto virtual replication with powershells3.amazonaws.com/zertodownload_docs/5.0u4/white...

134
Automating Zerto Virtual Replication with PowerShell & REST APIs Whitepaper VERSION 3.0 NOVEMBER 2017

Upload: others

Post on 10-Jul-2020

20 views

Category:

Documents


1 download

TRANSCRIPT

AutomatingZertoVirtualReplicationwithPowerShell&RESTAPIsWhitepaper

VERSION3.0NOVEMBER2017

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 2 OF 134

Table of Contents 1 INTRODUCTION................................................................................................................................................4

1.1 UseCases...................................................................................................................................................................41.2 RESTAPIs...................................................................................................................................................................41.3 LegalDisclaimer.........................................................................................................................................................4

2 BASICS&BESTPRACTICES..................................................................................................................................5

2.1 Requirements............................................................................................................................................................52.2 UsingVariables&Arrays...........................................................................................................................................52.3 EncryptingPasswords................................................................................................................................................62.4 ScriptingBestPractices.............................................................................................................................................62.5 Transcripts.................................................................................................................................................................62.6 LoadingModules.......................................................................................................................................................72.7 BypassingCertificateWarnings.................................................................................................................................72.8 EstablishingAPISessions...........................................................................................................................................82.9 FullStartofScriptExample.......................................................................................................................................9

3 QUERYING & REPORTING..............................................................................................................................11

3.1 UseCases.................................................................................................................................................................113.1 ListingUnprotectedVMs.........................................................................................................................................113.2 UsingUnProtectedVMIDs......................................................................................................................................113.3 ListingProtectedVMs&VPGs.................................................................................................................................123.4 LongTermRPO&StorageReportingtoCSV...........................................................................................................133.5 ResourceReports....................................................................................................................................................153.6 ResourceReportUseCases.....................................................................................................................................173.7 VPG,VM,VDISK,VNIC&Re-IPSettingsReport.......................................................................................................18

4 DAILY EMAIL REPORTS..................................................................................................................................27

4.1 UseCases.................................................................................................................................................................274.2 DesignMethodology...............................................................................................................................................274.3 DailyEmailReport...................................................................................................................................................27

5 AUTOMATING DEPLOYMENT......................................................................................................................67

5.1 UseCases.................................................................................................................................................................675.2 BulkAutomatedVRADeployment..........................................................................................................................675.3 BulkAutomatedVPGCreation–ZVMOnly.............................................................................................................705.4 BulkAutomatedVPGCreation–ZVM&ZCM.........................................................................................................765.5 BulkAutomatedVPGCreationwithBootGroups&Re-IP–ZVMOnly...................................................................82

6 AUTOMATING VM PROTECTION.................................................................................................................89

6.1 UseCases.................................................................................................................................................................896.2 AutomatingVMProtectionbyvSphereFolder-ZVMOnly.....................................................................................896.3 AutomatingVMProtectionbyvSphereFolder-ZVM&ZCM.................................................................................976.4 AutomatingVMProtectionwithvRealizeOrchestrator........................................................................................1046.5 AddingVMstoVPGs..............................................................................................................................................105

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 3 OF 134

7 BULK EDIT OPERATIONS..............................................................................................................................107

7.1 BulkVPGNameChanging......................................................................................................................................1077.2 BulkEditingVMNICSettingsIncludingRe-IP&PortGroups................................................................................111

8 SCHEDULING OFFSITE CLONES...................................................................................................................119

8.1 UseCases...............................................................................................................................................................1198.2 DesignMethodology.............................................................................................................................................1198.3 ScheduledOffsiteClone........................................................................................................................................120

9 TROUBLESHOOTING....................................................................................................................................134

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 4 OF 134

1 INTRODUCTION

1.1 Use Cases

This document gives an overview of how to utilize Zerto Virtual Replication REST APIs with PowerShell to automate your virtual infrastructure. In turn, this enables the reduction of manual processes and the realization of the full benefits of software defined replication and recovery. Highlights of key use cases covered within this document include:

• Automating VM protection • Automating VM protection with vRealize Orchestrator • Bulk VRA deployment • Bulk VPG configuration • Scheduling Offsite Clones • Daily email reports • Bulk Re-IP addressing

Also included is how to obtain reporting on:

• Long term RPO statistics • VMs by top journal and recovery storage usage • Protected and unprotected VMs • VMs by target Hosts for recovery balancing • VMs by average bandwidth utilization • VMs by datastore • VMs by CPU and RAM allocation

The examples given have been developed to meet the requirements of Zerto cloud and enterprise environments ranging from 100s to thousands of VMs. The font size of the examples given are optimized depending on the maximum line length to enable copying and pasting directly from the PDF into your PowerShell scripts.

1.2 REST APIs

The REST APIs utilized in the examples are all included within the Zerto Virtual Manager documentation set. The document does not intend to give examples for all the available APIs. Its purpose is to give examples of the most commonly requested use cases and give you the information and knowledge to customize and create your own automation scripts.

1.3 Legal Disclaimer

ALL EXAMPLES AND SCRIPTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. THE AUTHOR AND ZERTO FURTHER DISCLAIM ALL IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR OF FITNESS FOR A PARTICULAR PURPOSE.

IN NO EVENT SHALL ZERTO, ITS AUTHORS, OR ANYONE ELSE INVOLVED IN THE CREATION, PRODUCTION, OR DELIVERY OF THE SCRIPTS BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SAMPLE SCRIPTS OR DOCUMENTATION, EVEN IF THE AUTHOR OR ZERTO HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE ENTIRE RISK ARISING OUT OF THE USE OR PERFORMANCE OF THE SAMPLE SCRIPTS AND DOCUMENTATION REMAINS WITH YOU.

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 5 OF 134

2 BASICS&BESTPRACTICES

2.1 Requirements

All the example scripts given in this document share a common set of minimum requirements. These are:

• ZVM Server Name, Username and password with permission to access the API of the ZVM • Network connectivity to the ZVM • Access permission to write in and create the directory specified for logging, or manually create the directory in advance • The PowerShell execution policy set to "Set-ExecutionPolcity unrestricted" • Use Foxit PDF Reader or Adobe PDF reader to open then copy examples from this PDF and maintain the script structure

Some example scripts also directly interact with the vCenter or a Zerto Cloud Manager (ZCM). For these examples, the following requirements must also be met:

• VMware PowerCLI, 5.5+, installed on the host running the script • vCenter Server Name, Username and password to establish as session using PowerCLI to the vCenter • Network connectivity to the vCenter • ZCM with a new service profile configured and applied to both site ZVMs (source and target)

2.2 Using Variables & Arrays

To store data for execution within PowerShell scripts it is important to use variables and arrays for efficiency. Variables allow a value to be used multiple times and it only needs to be changed once, making editing scripts easier. For ease of use the same variables will be used in all examples in this document, these are:

$ZertoServer="192.168.0.31"$ZertoPort="9669"[email protected]$ZertoPassword="Password123!"$vCenterServer="192.168.0.81"[email protected]$vCenterPassword="Password123!"

Arrays are used in many examples in this document. These are used to build tables of data obtained from the APIs and multiple data sources simultaneously, like vSphere PowerCLI queries. The array is then used to export data to CSVs or to perform multiple actions within the script. Arrays are created line by line with the required values which are then added together. Below as an example of how to create a simple array:

$Value1=“FirstValue”$Value2=“SecondValue”$Array=@()$ArrayLine=new-objectPSObject$ArrayLine|Add-Member-MemberTypeNoteProperty-Name"Value1"-Value$Value1$ArrayLine|Add-Member-MemberTypeNoteProperty-Name"Value2"-Value$Value2$Array+=$ArrayLine$Array

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 6 OF 134

2.3 Encrypting Passwords

In all the examples provided in this document the passwords are stored using plain text within your PowerShell scripts. This is often against company policy and so is only done for ease of testing and there are multiple ways to remove the use of plain text passwords. To encrypt the passwords, within the machine and user running the script, follow the steps in the link below:

http://www.adminarsenal.com/admin-arsenal-blog/secure-password-with-powershell-encrypting-credentials-part-1/

Alternatively, if you need to run the script across multiple users and machines you can use a secure key:

http://www.adminarsenal.com/admin-arsenal-blog/secure-password-with-powershell-encrypting-credentials-part-2/

2.4 Scripting Best Practices

Scripting and automating tasks in a production environment can introduce an element of risk if care is not taken to ensure the scripts have been tested. To mitigate this risk, follow the below best practices:

1. Use Test VMs where applicable for initial testing 2. Use a demo lab or separate vSphere environment if available for testing 3. Test scripts using PowerShell ISE to aid with troubleshooting before scheduling a script 4. Use transcripts to maintain audit trails and aid with troubleshooting 5. Create descriptive comments throughout scripts to make it easier to understand and edit 6. Use version controls on scripts with last known good configurations 7. Manage and schedule scripts from a central server 8. Document scripts and automated processes 9. Ensure IT teams know how to enable and disable scripts

2.5 Transcripts

In all examples of significant length transcripts are used to log the script being run and capture any issues upon execution. A logical naming and folder structure is also utilized. To start a transcript:

#################################################Settinglogdirectoryandstartingtranscriptlogging################################################$LogDataDir=“C:\LogFolder\”$CurrentMonth=get-date-formatMM.yy$CurrentTime=get-date-formathh.mm.ss$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\BulkVPGCreationLog-"+$CurrentTime+".txt"#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDirif($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber

To stop the transcript at the end of the script use:

#################################################Stoppinglogging################################################Stop-Transcript

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 7 OF 134

2.6 Loading Modules

When working with the Zerto API and PowerShell it is often required to also interact with the vCenter using PowerCLI. We recommend installing PowerCLI via the PowerShell Galery using the method described in VMware’s Blog located here https://blogs.vmware.com/PowerCLI/2017/04/powercli-install-process-powershell-gallery.html. Using this method, PowerCLI will be available when needed in your scripts without having to load additional modules. The example scripts included in this document assume that you are using this method to include required PowerShell modules. Occassionally, a module will not be available via the PowerShell Galery and will need to be called manually, such is the case when working with the Zerto PowerShell CMDLETs. In cases like this, we suggest using the following method to load the module required:

functionLoadSnapin{param($PSSnapinName)if(!(Get-PSSnapin|where{$_.Name-eq$PSSnapinName})){Add-pssnapin-name$PSSnapinName}}#LoadingsnapinsandmodulesLoadSnapin-PSSnapinName"Zerto.PS.Commands"

2.7 Bypassing Certificate Warnings

By default, ZVR is installed with a self-generated certificate. If this is not replaced the API authentication will fail unless the same script has connected to the vCenter server using PowerCLI, which the ZVM is connected to, or if the default certificate behaviour of the script has been changed as per:

#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPIwithnon-trustedcerts################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 8 OF 134

2.8 Establishing API Sessions

To utilize ZVR REST APIs an authenticated session is required at the start of each PowerShell script as per:

#################################################BuildingZertoAPIstringandinvokingAPI################################################$BaseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$BaseURL+"session/add"$AuthInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$AuthInfo=[System.Text.Encoding]::UTF8.GetBytes($AuthInfo)$AuthInfo=[System.Convert]::ToBase64String($AuthInfo)$Headers=@{Authorization=("Basic{0}"-f$AuthInfo)}$SessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$Headers-MethodPOST-Body$SessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$ZertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 9 OF 134

2.9 Full Start of Script Example

Following is a sample start of script that connects to the ZVM API:

#################################################Configurethevariablesbelow################################################$LogDataDir="C:\LogFolder\"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#################################################Settinglogdirectoryandstartingtranscriptlogging################################################$CurrentMonth=get-date-formatMM.yy$CurrentTime=get-date-formathh.mm.ss$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\BulkVPGCreationLog-"+$CurrentTime+".txt"#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDirif($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPIwithoutconnectingtovsphereusingPowerCLI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$BaseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$BaseURL+"session/add"$AuthInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$AuthInfo=[System.Text.Encoding]::UTF8.GetBytes($AuthInfo)$AuthInfo=[System.Convert]::ToBase64String($AuthInfo)$Headers=@{Authorization=("Basic{0}"-f$AuthInfo)}$SessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$Headers-MethodPOST-Body$SessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 10 OF 134

$ZertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}

Following is a sample start of script that connects to the ZVM API and a vCenter using PowerCLI:

#################################################ConfigurethevariablesbelowusingtheProductionvCenter&ZVM################################################$LogDataDir="C:\LogFolder\"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"$vCenterServer="192.168.0.81"$vCenterUser="[email protected]"$vCenterPassword="Zerto1234!"#################################################Settinglogdirectoryforengineandcurrentmonth################################################$CurrentMonth=get-date-formatMM.yy$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentTime=get-date-formathh.mm.ss#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDir$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\VPGCreationLog-"+$CurrentTime+".txt"if($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################ConnectingtovCenter-requiredforsuccessfulauthenticationwithZertoAPI################################################connect-viserver-Server$vCenterServer-User$vCenterUser-Password$vCenterPassword#################################################BuildingZertoAPIstringandinvokingAPI################################################$BaseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$BaseURL+"session/add"$AuthInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$AuthInfo=[System.Text.Encoding]::UTF8.GetBytes($AuthInfo)$AuthInfo=[System.Convert]::ToBase64String($AuthInfo)$Headers=@{Authorization=("Basic{0}"-f$AuthInfo)}$SessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$Headers-MethodPOST-Body$SessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$ZertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 11 OF 134

3 QUERYING & REPORTING

3.1 Use Cases

A common use case of the API is to extend and configure custom reporting of data over long term periods, beyond what the 24 hours retained within the Zerto Virtual Manager Database. The following APIs can be used to extract data:

• https://zvm_ip:port/v1/events • https://zvm_ip:port/v1/alerts • https://zvm_ip:port/v1/tasks • https://zvm_ip:port/v1/vms • https://zvm_ip:port/v1/vpgs • https://zvm_ip:port/v1/vpgSettings • https://zvm_ip:port/v1/vras • https://zvm_ip:port/ZvmService/ResourcesReport

With the ability to extract any data from the API the use cases are endless. Following are some commonly requested examples and how to use them. All the examples given presume you have already created the start of the script as the per section 2.9.

3.1 Listing Unprotected VMs

To list all VMs not currently protected by Zerto use the below script:

#GetSiteIdentifier$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$ZertosessionHeader-ContentType$TypeJSON$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#UsingSiteIdentifiertogetalistonunprotectedVMs$UnprotectedVMListURL=$BaseURL+"virtualizationsites/"+$LocalSiteIdentifier+"/vms"$UnprotectedVMList=Invoke-RestMethod-Uri$UnprotectedVMListURL-TimeoutSec100-Headers$ZertosessionHeader-ContentType$TypeJSON#Buildingtable$UnprotectedVMListTable=$UnprotectedVMList|selectVmIdentifier,VmName$UnprotectedVMListTable|format-table-AutoSize

3.2 Using UnProtected VM IDs

When utilizing the Zerto API for unprotected VMs a list of VM names and Zerto VM identifiers is returned. To get further information on the VMs from the vCenter the VM ID can be calculated using the following example:

#ConnecttothesourcevCenterfirst#Creatingarray$UnprotectedVMArray=@()#BuildthelistofVMsusingexamplein3.1foreach($VMin$UnprotectedVMList){#GettingvCenterVMIDfromZVRID$VMName=$VM.VmName$VMZVRID=$VM.VmIdentifier$Separator="."$VMZVRIDSplit=$VMZVRID.split($Separator)$VMID=$VMZVRIDSplit[1]$VMID="VirtualMachine-"+$VMID#UsingvCenterVMIDtogetmoreinfo$VMCluster=get-vm-Id$VMID|Get-Cluster|select-expandpropertyName-First1$VMInfo=get-vm-Id$VMID|selectFolder,NumCPU,MemoryGB,HardDisks,NetworkAdapters,UsedSpaceGB-First1$VMFolder=$VMInfo.Folder$VMNumCPU=$VMInfo.NumCpu

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 12 OF 134

$VMMemoryGB=$VMInfo.MemoryGB$VMMemoryGB=[math]::Round($VMMemoryGB,2)$VMHardDisks=$VMInfo.HardDisks.Count$VMNICS=$VMInfo.NetworkAdapters.Count$VMUsedSpaceGB=$VMInfo.UsedSpaceGB$VMUsedSpaceGB=[math]::Round($VMUsedSpaceGB,2)#Buildingarrayline$UnprotectedVMArrayLine=new-objectPSObject$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value"$VMName"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMFolder"-Value"$VMFolder"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCluster"-Value"$VMCluster"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"NumCPU"-Value"$VMNumCPU"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"MemoryGB"-Value"$VMMemoryGB"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"NICS"-Value"$VMNICS"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"HardDisks"-Value"$VMHardDisks"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"UsedSpaceGB"-Value"$VMUsedSpaceGB"$UnprotectedVMArray+=$UnprotectedVMArrayLine}

3.3 Listing Protected VMs & VPGs

OnceZertoisconfiguredacommonusecaseistoretrievealistofprotectedVMswiththeirassociatedVPGsforemailreportsorforintegrationintoaseparateplatform.Thiscanbeachievedwith:

#QueryingAPI$VMListURL=$BaseURL+"vms"$VMList=Invoke-RestMethod-Uri$VMListURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$VMListTable=$VMList|selectVmName,VpgName,UsedStorageInMB,SourceSite,TargetSite,Priority$VMListTable|format-table-AutoSize

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 13 OF 134

3.4 Long Term RPO & Storage Reporting to CSV

A common request from customers is to maintain RPO history over many months. This can be achieved with the below example which creates a new CSV file per ZORG per month and lists all of the VPGs assigned. If no ZORG is defined a NoZORG CSV is created. The script can be scheduled to poll the information on an interval desired, but an interval of 5 minutes’ max is recommended to avoid data ballooning:

#################################################Configurethevariablesbelow################################################$ExportDataDir="C:\ZVRAPIScript\"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#################################################Nothingtoconfigurebelowhere#################################################################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPIwithoutconnectingtovsphereusingPowerCLI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$BaseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$BaseURL+"session/add"$AuthInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$AuthInfo=[System.Text.Encoding]::UTF8.GetBytes($AuthInfo)$AuthInfo=[System.Convert]::ToBase64String($AuthInfo)$Headers=@{Authorization=("Basic{0}"-f$AuthInfo)}$SessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$Headers-MethodPOST-Body$SessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$ZertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeXML}#################################################Runningmainbodyofscript################################################$CurrentMonth=get-date-formatMM.yy$CurrentExportDataDir=$ExportDataDir+$CurrentMonth#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentExportDataDir

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 14 OF 134

if($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentExportDataDir}#BuildListofVPGs$vpgListApiUrl=$baseURL+"vpgs"$vpgList=Invoke-RestMethod-Uri$vpgListApiUrl-TimeoutSec100-Headers$ZertoSessionHeader-ContentType$TypeXML#BuildingProtectiongrouparrayandgettingdateandtimeofthequeryforinsertionintotheCSV$zertoprotectiongrouparray=@()#Changethedatetimeformatsbelowifneeded$zertoprotectiongrouparrayTimeStampDate=get-date-formatd$zertoprotectiongrouparrayTimeStampTime=get-date-formatHH:mm:ss#Buildingthearrayofdata$zertoprotectiongrouparray=$vpgList.ArrayOfVpgApi.VpgApi|Select-ObjectOrganizationName,vpgname,ActualRPO,Status,vmscount,ProvisionedStorageInMB,UsedStorageInMB#LoggingdataperVPGforeach($_in$zertoprotectiongrouparray){$CurrentOrganizationName=$_.OrganizationName#AssigningaZORGcalled"NoZORG"ifonedoesnotexistif($CurrentOrganizationName-eq""){$CurrentOrganizationName="NoZORG"}#BuildinglogfilenamefortheZORGfound$CurrentCSVName=$CurrentExportDataDir+"\"+"$CurrentOrganizationName"+"-"+$CurrentMonth+"-ZertoAPIExport.CSV"#SettingcurrentvaluesforinsertionintotheCSVfileusinganarray$CurrentVPGArray=@()$CurrentVPGArrayLine=new-objectPSObject$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"Date"-Value$zertoprotectiongrouparrayTimeStampDate$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"Time"-Value$zertoprotectiongrouparrayTimeStampTime$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$_.vpgname$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"ZORG"-Value$CurrentOrganizationName$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"ActualRPO"-Value$_.ActualRPO$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"Status"-Value$_.Status$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"vmscount"-Value$_.vmscount$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"ProvisionedStorage"-Value$_.ProvisionedStorageInMB$CurrentVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"UsedStorage"-Value$_.UsedStorageInMB$CurrentVPGArray+=$CurrentVPGArrayLine#TestingtoseeifCSValreadyexists$CurrentCSVNameTestPath=test-path$CurrentCSVName#IfCSVexisttestFalsecreatingtheCSVwithnoappendif($CurrentCSVNameTestPath-eq$False){$CurrentVPGArray|export-csv-path$CurrentCSVName-NoTypeInformation}#IfCSVexisttestTrueappendingtotheexistingCSVif($CurrentCSVNameTestPath-eq$True){$CurrentVPGArray|export-csv-path$CurrentCSVName-NoTypeInformation-Append}#EndofperVPGactionsbelow}#EndofperVPGactionsabove

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 15 OF 134

3.5 Resource Reports

The Resource Report API can be used to obtain a significant amount of data within Zerto on the configuration and usage of protected VMs. Resource reports are generated and queried from the recovery site ZVM, can also be exported manually from the ZVM into an xls file, and only show data for VPGs replicating into the ZVM specified. The frequency on which resource report data is taken is controlled by the advanced settings within the ZVM, it is not a live view on the data unlike every other API used. In the examples given a 24-hour report sample is utilized for simplicity. The following data can be obtained from the API:

• ActiveGuestMemoryInMB - The active memory of the virtual machine. • BandwidthInBytes - The average bandwidth used between two consecutive samples. • ConsumedHostMemoryInMB - The amount of host memory consumed by the virtual machine. • CpuLimitInMhz - The maximum MHz available for the CPUs in the virtual machine. • CpuReservedInMhz - The MHz reserved for use by the CPUs in the virtual machine. • CpuUsedInMhz - The MHz used by the CPUs in the virtual machine. • CrmId - The CRM identifier specified in Zerto Cloud Manager for an organization that uses a cloud service provider for

recovery. • MemoryInMB - The virtual machine defined memory. • MemoryLimitInMB - The upper limit for this virtual machine’s memory allocation. • MemoryReservedInMB - The guaranteed memory allocation for this virtual machine. • NumberOfvCpu - The number of CPUs for the virtual machine. • NumberOfVolumes - The number of volumes attached to the virtual machine. • RecoveryJournalProvisionedStorageInGB - The amount of provisioned journal storage for the virtual machine. • RecoveryJournalUsedStorageInGB - The amount of used journal storage for the virtual machine. • RecoveryVolumesProvisionedStorageInGB - The amount of provisioned storage for the virtual machine in the target site.

This value is the sum of volumes’ provisioned size. • RecoveryVolumesUsedStorageInGB - The amount of used storage for the virtual machine in the target site. • ServiceProfile - The service profile used by the VPG. • SourceCluster - The source cluster name hosting the virtual machine. • SourceHost - The source ESX/ESXi name hosting the virtual machine. • SourceOrgVDC - The name of the source vDC Org. • SourceResourcePool - The source resource pool name hosting the virtual machine. • SourceSite - The source protected site name, defined in the Zerto User Interface. • SourceVCDOrg - The name of the source vCD Org. • SourceVolumesProvisionedStorageInGB - The amount of provisioned storage for the virtual machine in the source site.

This value is the sum of volumes’ provisioned size. • SourceVolumesUsedStorageInGB - The amount of used storage for the virtual machine in the source site. This value is the

sum of the volumes’ used size. • SourceVraName - The name of the source VRA used to send data to the recovery site. • StorageProfile - The target vCD storage profile used. • TargetCluster - The target cluster name hosting the virtual machine. • TargetDatastores - The target datastore used for the virtual machine if it is recovered. • TargetHost - The target ESX/ESXi name hosting the virtual machine when it is recovered. • TargetOrgVDC - The name of the target vDC Org • TargetResourcePool - The target resource pool name where the virtual machine will be recovered. • TargetSite - The target site name, defined in the Zerto User Interface.

TargetVCDOrg - The name of the target vCD Org. TargetVraName - The name of the recovery VRA managing the recovery.

• ThroughputInBytes - The average throughput used between two consecutive samples. • Timestamp - The date and time the resource information was collected. • VmHardwareVersion - The VMware hardware version. • VmId - The internal virtual machine identifier. The identifier comprises the server identifier and the virtual machine moref,

with the format, serverid. moref. • VmName - The name of the virtual machine. • VpgName - The name of the VPG.

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 16 OF 134

• VpgType - The VPG Type: VC2VC – vCenter to vCenter, VC2VCD – vCenter to vCloud Director, VCD2VCD – vCloud Director to vCloud Director, VCD2VC – vCloud Director to vCenter.

• ZORG - An organization set up in the Zerto Cloud Manager using a cloud service provider for recovery. Following is an example querying a subset of the above data: #################################################Configurethevariablesbelow,usetherecoverysiteZVM################################################$ZertoServer="192.168.0.32"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPIwithoutconnectingtovsphereusingPowerCLI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$BaseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$BaseURL+"session/add"$AuthInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$AuthInfo=[System.Text.Encoding]::UTF8.GetBytes($AuthInfo)$AuthInfo=[System.Convert]::ToBase64String($AuthInfo)$Headers=@{Authorization=("Basic{0}"-f$AuthInfo)}$SessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$Headers-MethodPOST-Body$SessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$ZertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#Settingenddatetobemidnight(asdefaulttimeis00:00)andstartdateismidnightlastnightfor24hoursofdata$StartDateTime=get-date-Formatyyyy-MM-dd$EndDateTime=(get-date).AddDays(1).ToString("yyyy-MM-dd")#QueryResourceReport$ResourceReportURLBase="https://"+$ZertoServer+":"+$ZertoPort+"/ZvmService/ResourcesReport/getSamples?fromTimeString="$ResourceReportURL=$ResourceReportURLBase+$StartDateTime+"&toTimeString="+$EndDateTime+"&startIndex=0&count=500"$ResourceReport=Invoke-RestMethod-Uri$ResourceReportURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$ResourceReportAPIArray=$ResourceReport.ArrayOfVmResourcesInfo.VmResourcesInfo#BuildingtablefromArraybyVpgName$ResourceReportAPITable=$ResourceReport|Sort-Object-PropertyVpgName|format-table-PropertyTimestamp,VpgName,VmName,TargetVraName$ResourceReportAPITable

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 17 OF 134

3.6 Resource Report Use Cases

Once you have successfully queried the resource report API many different use cases can be fulfilled. Note: when copying and pasting the below examples delete the line break inserted after “-Property” for the script to function correctly: #ProtectedVMsbymostRecoveryJournalUsedStorageInGBusage$ResourceReportAPITable=$ResourceReport|Sort-Object-PropertyRecoveryJournalUsedStorageInGB-Descending|format-table-PropertyTimestamp,VmName,VpgName,RecoveryJournalUsedStorageInGB,RecoveryVolumesUsedStorageInGB$ResourceReportAPITable#ProtectedVMsbymostRecoveryVolumesStorageInGBusage$ResourceReportAPITable=$ResourceReport|Sort-Object-PropertyRecoveryVolumesUsedStorageInGB-Descending|format-table-PropertyTimestamp,VmName,VpgName,RecoveryVolumesProvisionedStorageInGB,RecoveryVolumesUsedStorageInGB$ResourceReportAPITable#ProtectedVMsbyCPUandRAMsize$ResourceReportAPITable=$ResourceReport|Sort-Object-PropertyNumberOfvCpu,MemoryInMB-Descending|format-table-PropertyTimestamp,VpgName,VmName,NumberOfvCpu,MemoryInMB,VmHardwareVersion$ResourceReportAPITable#ProtectedVMsbytargethost,thenbyCPUandRAMsize,usefulforbalancingrecovery$ResourceReportAPITable=$ResourceReport|Sort-Object-PropertyTargetHost,NumberOfvCpu,MemoryInMB-Descending|format-table-PropertyTimestamp,VpgName,VmName,TargetHost,NumberOfvCpu,MemoryInMB,VmHardwareVersion$ResourceReportAPITable#VMsreplicatingtoaVRAwithnumberofvolumesperVM,usefulforbalancingreplication$ResourceReportAPITable=$ResourceReport|Where-Object{$_.TargetVraName-eq"Z-VRA-192.168.0.14"}|format-table-PropertyTimestamp,VmName,VpgName,TargetVraName,NumberOfVolumes$ResourceReportAPITable#ProtectedVMswithaspecificVMhardwareversion$ResourceReportAPITable=$ResourceReport|Where-Object{$_.VmHardwareVersion-eq"vmx-08"}|format-table-PropertyTimestamp,VmName,VpgName,VmHardwareVersion$ResourceReportAPITable#ProtectedVMsbyhighestaveragebandwidthusage(derivedfromtimebetweensamples)$ResourceReportAPITable=$ResourceReport|Sort-Object-PropertyBandwidthInBytes-Descending|format-table-PropertyTimestamp,VmName,VpgName,BandwidthInBytes$ResourceReportAPITable#ProtectedVMstoaspecificdatastore:$ResourceReportAPITable=$ResourceReport|Where-Object{$_.TargetDatastores-eq"ESXi2SATA"}|format-table-PropertyTimestamp,VmName,TargetDatastores,RecoveryVolumesUsedStorageInGB,RecoveryJournalUsedStorageInGB$ResourceReportAPITable

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 18 OF 134

3.7 VPG, VM, VDISK, VNIC & Re-IP Settings Report

To report on all the settings configured within a VPG the Edit VPG API must be utilized. The API mimics the ZVM GUI which requires a VPG to be edited to view information such as re-ip settings per VM NIC. The example script creates 4 table arrays containing the report data. These are VPGArray, VMArray, VMVolumeArray and VMNICArray. 4 tables are required as multiple VMs can exist in a VPG and multiple vDISKs/VNICs can exist in a VM. The VPGidentifier and VMIdentifiers are used to link the table entries logically. Below are the example outputs in a CSV file from each of the 4 tables:

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 19 OF 134

To build the example CSVs use the below script: ################################################

################################################

# Configure the variables below

################################################

$ExportDataDir = "C:\ZVRAPIScript\"

$ZertoServer = "192.168.0.31"

$ZertoPort = "9669"

$ZertoUser = "[email protected]"

$ZertoPassword = "Zerto1234!"

########################################################################################################################

# Nothing to configure below this line - Starting the main function of the script

########################################################################################################################

################################################

# Setting certificate exception to prevent authentication issues to the ZVM

################################################

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

################################################

# Building Zerto API string and invoking API

################################################

$baseURL = "https://" + $ZertoServer + ":"+$ZertoPort+"/v1/"

# Authenticating with Zerto APIs

$xZertoSessionURL = $baseURL + "session/add"

$authInfo = ("{0}:{1}" -f $ZertoUser,$ZertoPassword)

$authInfo = [System.Text.Encoding]::UTF8.GetBytes($authInfo)

$authInfo = [System.Convert]::ToBase64String($authInfo)

$headers = @{Authorization=("Basic {0}" -f $authInfo)}

$sessionBody = '{"AuthenticationMethod": "1"}'

$TypeJSON = "application/json"

$TypeXML = "application/xml"

Try

{

$xZertoSessionResponse = Invoke-WebRequest -Uri $xZertoSessionURL -Headers $headers -Method POST -Body $sessionBody -ContentType $TypeJSON

}

Catch {

Write-Host $_.Exception.ToString()

$error[0] | Format-List -Force

}

# Extracting x-zerto-session from the response, and adding it to the actual API

$xZertoSession = $xZertoSessionResponse.headers.get_item("x-zerto-session")

$zertoSessionHeader_JSON = @{"x-zerto-session"=$xZertoSession; "Accept"=$TypeJSON }

$zertoSessionHeader_XML = @{"x-zerto-session"=$xZertoSession; "Accept"=$TypeXML }

################################################

# Creating Arrays for populating ZVM info from the API

################################################

$VPGArray = @()

$VMArray = @()

$VMVolumeArray = @()

$VMNICArray = @()

################################################

# Creating VPGArray, VMArray, VMVolumeArray, VMNICArray

################################################

# URL to create VPG settings

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 20 OF 134

$CreateVPGURL = $baseURL+"vpgSettings"

# Build List of VPGs

$vpgListApiUrl = $baseURL+"vpgs"

$vpgList = Invoke-RestMethod -Uri $vpgListApiUrl -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

# Build List of VMs

$vmListApiUrl = $baseURL+"vms"

$vmList = Invoke-RestMethod -Uri $vmListApiUrl -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

# Select IDs from the API array

$zertoprotectiongrouparray = $vpgList.ArrayOfVpgApi.VpgApi | Select-Object OrganizationName,vpgname,vmscount,vpgidentifier

$vmListarray = $vmList.ArrayOfVmApi.VmApi | select-object *

################################################

# Starting for each VPG action of collecting ZVM VPG data

################################################

foreach ($VPGLine in $zertoprotectiongrouparray)

{

$VPGidentifier = $VPGLine.vpgidentifier

$VPGOrganization = $VPGLine.OrganizationName

$VPGVMCount = $VPGLine.VmsCount

$JSON =

"{

""VpgIdentifier"":""$VPGidentifier""

}"

################################################

# Posting the VPG JSON Request to the API

################################################

Try

{

$VPGSettingsIdentifier = Invoke-RestMethod -Method Post -Uri $CreateVPGURL -Body $JSON -ContentType $TypeJSON -Headers $zertoSessionHeader_JSON

$ValidVPGSettingsIdentifier = $true

}

Catch {

$ValidVPGSettingsIdentifier = $false

}

################################################

# Getting VPG settings from API

################################################

# Skipping if unable to obtain valid VPG setting identifier

if ($ValidVPGSettingsIdentifier -eq $true)

{

$VPGSettingsURL = $baseURL+"vpgSettings/"+$VPGSettingsIdentifier

$VPGSettings = Invoke-RestMethod -Uri $VPGSettingsURL -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting recovery site ID (needed anyway for network settings)

$VPGRecoverySiteIdentifier = $VPGSettings.Basic.RecoverySiteIdentifier

# Getting site info

$VISitesURL = $baseURL+"virtualizationsites"

$VISitesCMD = Invoke-RestMethod -Uri $VISitesURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting network info

$VINetworksURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/networks"

$VINetworksCMD = Invoke-RestMethod -Uri $VINetworksURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting datastore info

$VIDatastoresURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/datastores"

$VIDatastoresCMD = Invoke-RestMethod -Uri $VIDatastoresURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting datastore cluster info

$VIDatastoreClustersURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/datastoreclusters"

$VIDatastoreClustersCMD = Invoke-RestMethod -Uri $VIDatastoreClustersURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting folder info

$VIFoldersURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/folders"

$VIFoldersCMD = Invoke-RestMethod -Uri $VIFoldersURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting cluster info

$VIClustersURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/hostclusters"

$VIClustersCMD = Invoke-RestMethod -Uri $VIClustersURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting host info

$VIHostsURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/hosts"

$VIHostsCMD = Invoke-RestMethod -Uri $VIHostsURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting resource pool info

$VIResourcePoolsURL = $baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/resourcepools"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 21 OF 134

$VIResourcePoolsCMD = Invoke-RestMethod -Uri $VIResourcePoolsURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting all VPG Settings

$VPGJournalHistoryInHours = $VPGSettings.Basic.JournalHistoryInHours

$VPGName = $VPGSettings.Basic.Name

$VPGPriortiy = $VPGSettings.Basic.Priority

$VPGProtectedSiteIdentifier = $VPGSettings.Basic.ProtectedSiteIdentifier

$VPGRpoInSeconds = $VPGSettings.Basic.RpoInSeconds

$VPGServiceProfileIdentifier = $VPGSettings.Basic.ServiceProfileIdentifier

$VPGTestIntervalInMinutes = $VPGSettings.Basic.TestIntervalInMinutes

$VPGUseWanCompression = $VPGSettings.Basic.UseWanCompression

$VPGZorgIdentifier = $VPGSettings.Basic.ZorgIdentifier

# Getting Boot Group IDs

$VPGBootGroups = $VPGSettings.BootGroups.BootGroups

$VPGBootGroupCount = $VPGSettings.BootGroups.BootGroups.Count

$VPGBootGroupNames = $VPGSettings.BootGroups.BootGroups.Name

$VPGBootGroupDelays = $VPGSettings.BootGroups.BootGroups.BootDelayInSeconds

$VPGBootGroupIdentifiers = $VPGSettings.BootGroups.BootGroups.BootGroupIdentifier

# Getting Journal info

$VPGJournalDatastoreClusterIdentifier = $VPGSettings.Journal.DatastoreClusterIdentifier

$VPGJournalDatastoreIdentifier = $VPGSettings.Journal.DatastoreIdentifier

$VPGJournalHardLimitInMB = $VPGSettings.Journal.Limitation.HardLimitInMB

$VPGJournalHardLimitInPercent = $VPGSettings.Journal.Limitation.HardLimitInPercent

$VPGJournalWarningThresholdInMB = $VPGSettings.Journal.Limitation.WarningThresholdInMB

$VPGJournalWarningThresholdInPercent = $VPGSettings.Journal.Limitation.WarningThresholdInPercent

# Getting Network IDs

$VPGFailoverNetworkID = $VPGSettings.Networks.Failover.Hypervisor.DefaultNetworkIdentifier

$VPGFailoverTestNetworkID = $VPGSettings.Networks.FailoverTest.Hypervisor.DefaultNetworkIdentifier

# Getting recovery info

$VPGDefaultDatastoreIdentifier = $VPGSettings.Recovery.DefaultDatastoreIdentifier

$VPGDefaultFolderIdentifier = $VPGSettings.Recovery.DefaultFolderIdentifier

$VPGDefaultHostClusterIdentifier = $VPGSettings.Recovery.DefaultHostClusterIdentifier

$VPGDefaultHostIdentifier = $VPGSettings.Recovery.DefaultHostIdentifier

$VPGResourcePoolIdentifier = $VPGSettings.Recovery.ResourcePoolIdentifier

# Getting scripting info

$VPGScriptingPreRecovery = $VPGSettings.Scripting.PreRecovery

$VPGScriptingPostRecovery = $VPGSettings.Scripting.PostRecovery

# Getting VM IDs in VPG

$VPGVMIdentifiers = $VPGSettings.VMs.VmIdentifier

################################################

# Translating Zerto IDs from VPG settings to friendly vSphere names

################################################

# Getting site names

$VPGProtectedSiteName = $VISitesCMD | Where-Object {$_.SiteIdentifier -eq $VPGProtectedSiteIdentifier} | select -ExpandProperty VirtualizationSiteName

$VPGRecoverySiteName = $VISitesCMD | Where-Object {$_.SiteIdentifier -eq $VPGRecoverySiteIdentifier} | select -ExpandProperty VirtualizationSiteName

# Getting network names

$VPGFailoverNetworkName = $VINetworksCMD | Where-Object {$_.NetworkIdentifier -eq $VPGFailoverNetworkID} | Select -ExpandProperty VirtualizationNetworkName

$VPGFailoverTestNetworkName = $VINetworksCMD | Where-Object {$_.NetworkIdentifier -eq $VPGFailoverTestNetworkID} | Select -ExpandProperty VirtualizationNetworkName

# Getting datastore cluster name

$VPGJournalDatastoreClusterName = $VIDatastoreClustersCMD | Where-Object {$_.DatastoreClusterIdentifier -eq $VPGJournalDatastoreClusterIdentifier} | select -ExpandProperty DatastoreClusterName

# Getting datastore names

$VPGDefaultDatastoreName = $VIDatastoresCMD | Where-Object {$_.DatastoreIdentifier -eq $VPGDefaultDatastoreIdentifier} | select -ExpandProperty DatastoreName

$VPGJournalDatastoreName = $VIDatastoresCMD | Where-Object {$_.DatastoreIdentifier -eq $VPGJournalDatastoreIdentifier} | select -ExpandProperty DatastoreName

# Getting folder name

$VPGDefaultFolderName = $VIFoldersCMD | Where-Object {$_.FolderIdentifier -eq $VPGDefaultFolderIdentifier} | select -ExpandProperty FolderName

# Getting cluster name

$VPGDefaultHostClusterName = $VIClustersCMD | Where-Object {$_.ClusterIdentifier -eq $VPGDefaultHostClusterIdentifier} | select -ExpandProperty VirtualizationClusterName

# Getting host name

$VPGDefaultHostName = $VIHostsCMD | Where-Object {$_.HostIdentifier -eq $VPGDefaultHostIdentifier} | select -ExpandProperty VirtualizationHostName

# Getting resource pool name

$VPGResourcePoolName = $VIResourcePoolsCMD | Where-Object {$_.ResourcePoolIdentifier -eq $VPGResourcePoolIdentifier} | select -ExpandProperty ResourcepoolName

################################################

# Adding all VPG setting info to $VPGArray

################################################

$VPGArrayLine = new-object PSObject

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value $VPGName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGidentifier" -Value $VPGidentifier

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 22 OF 134

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGOrganization" -Value $VPGOrganization

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGVMCount" -Value $VPGVMCount

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGPriortiy" -Value $VPGPriortiy

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGProtectedSiteName" -Value $VPGProtectedSiteName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGProtectedSiteIdentifier" -Value $VPGProtectedSiteIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGRecoverySiteName" -Value $VPGRecoverySiteName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGRecoverySiteIdentifier" -Value $VPGRecoverySiteIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGRpoInSeconds" -Value $VPGRpoInSeconds

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGServiceProfileIdentifier" -Value $VPGServiceProfileIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGTestIntervalInMinutes" -Value $VPGTestIntervalInMinutes

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGUseWanCompression" -Value $VPGUseWanCompression

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGZorgIdentifier" -Value $VPGZorgIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGBootGroupCount" -Value $VPGBootGroupCount

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGBootGroupNames" -Value $VPGBootGroupNames

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGBootGroupDelays" -Value $VPGBootGroupDelays

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGBootGroupIdentifiers" -Value $VPGBootGroupIdentifiers

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalHistoryInHours" -Value $VPGJournalHistoryInHours

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalDatastoreClusterName" -Value $VPGJournalDatastoreClusterName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalDatastoreClusterIdentifier" -Value $VPGJournalDatastoreClusterIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalDatastoreName" -Value $VPGJournalDatastoreName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalDatastoreIdentifier" -Value $VPGJournalDatastoreIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalHardLimitInMB" -Value $VPGJournalHardLimitInMB

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalHardLimitInPercent" -Value $VPGJournalHardLimitInPercent

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalWarningThresholdInMB" -Value $VPGJournalWarningThresholdInMB

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGJournalWarningThresholdInPercent" -Value $VPGJournalWarningThresholdInPercent

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGFailoverNetworkName" -Value $VPGFailoverNetworkName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGFailoverNetworkID" -Value $VPGFailoverNetworkID

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGFailoverTestNetworkName" -Value $VPGFailoverTestNetworkName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGFailoverTestNetworkID" -Value $VPGFailoverTestNetworkID

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultDatastoreName" -Value $VPGDefaultDatastoreName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultDatastoreIdentifier" -Value $VPGDefaultDatastoreIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultFolderName" -Value $VPGDefaultFolderName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultFolderIdentifier" -Value $VPGDefaultFolderIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultHostClusterName" -Value $VPGDefaultHostClusterName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultHostClusterIdentifier" -Value $VPGDefaultHostClusterIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultHostName" -Value $VPGDefaultHostName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGDefaultHostIdentifier" -Value $VPGDefaultHostIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGResourcePoolName" -Value $VPGResourcePoolName

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGResourcePoolIdentifier" -Value $VPGResourcePoolIdentifier

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGScriptingPreRecovery" -Value $VPGScriptingPreRecovery

$VPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGScriptingPostRecovery" -Value $VPGScriptingPostRecovery

$VPGArray += $VPGArrayLine

################################################

# Starting for each VM ID action for collecting ZVM VM data

################################################

foreach ($_ in $VPGVMIdentifiers)

{

$VMIdentifier = $_

# Get VMs settings

$GetVMSettingsURL = $baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier

$GetVMSettings = Invoke-RestMethod -Method Get -Uri $GetVMSettingsURL -TimeoutSec 100 -Headers $zertoSessionHeader_JSON -ContentType $TypeJSON

# Getting the VM name and disk usage

$VMNameArray = $vmListarray | where-object {$_.VmIdentifier -eq $VMIdentifier} | Select-Object *

$VMName = $VMNameArray.VmName

$VMProvisionedStorageInMB = $VMNameArray.ProvisionedStorageInMB

$VMUsedStorageInMB = $VMNameArray.UsedStorageInMB

# Setting variables from the API

$VMVolumeCount = $GetVMSettings.Volumes.Count

$VMNICCount = $GetVMSettings.Nics.Count

$VMBootGroupIdentifier = $GetVMSettings.BootGroupIdentifier

$VMJournalDatastoreClusterIdentifier = $GetVMSettings.Journal.DatastoreClusterIdentifier

$VMJournalDatastoreIdentifier = $GetVMSettings.Journal.DatastoreIdentifier

$VMJournalHardLimitInMB = $GetVMSettings.Journal.Limitation.HardLimitInMB

$VMJournalHardLimitInPercent = $GetVMSettings.Journal.Limitation.HardLimitInPercent

$VMJournalWarningThresholdInMB = $GetVMSettings.Journal.Limitation.WarningThresholdInMB

$VMJournalWarningThresholdInPercent = $GetVMSettings.Journal.Limitation.WarningThresholdInPercent

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 23 OF 134

$VMDatastoreClusterIdentifier = $GetVMSettings.Recovery.DatastoreClusterIdentifier

$VMDatastoreIdentifier = $GetVMSettings.Recovery.DatastoreIdentifier

$VMFolderIdentifier = $GetVMSettings.Recovery.FolderIdentifier

$VMHostClusterIdentifier = $GetVMSettings.Recovery.HostClusterIdentifier

$VMHostIdentifier = $GetVMSettings.Recovery.HostIdentifier

$VMResourcePoolIdentifier = $GetVMSettings.Recovery.ResourcePoolIdentifier

################################################

# Translating Zerto IDs from VM settings to friendly vSphere names

################################################

# Getting boot group

$VMBootGroupName = $VPGBootGroups | Where-Object {$_.BootGroupIdentifier -eq $VMBootGroupIdentifier} | select -ExpandProperty Name

$VMBootGroupDelay = $VPGBootGroups | Where-Object {$_.BootGroupIdentifier -eq $VMBootGroupIdentifier} | select -ExpandProperty BootDelayInSeconds

# Getting datastore cluster name

$VMJournalDatastoreClusterName = $VIDatastoreClustersCMD | Where-Object {$_.DatastoreClusterIdentifier -eq $VMJournalDatastoreClusterIdentifier} | select -ExpandProperty DatastoreClusterName

$VMDatastoreClusterName = $VIDatastoreClustersCMD | Where-Object {$_.DatastoreClusterIdentifier -eq $VMDatastoreClusterIdentifier} | select -ExpandProperty DatastoreClusterName

# Getting datastore name

$VMJournalDatastoreName = $VIDatastoresCMD | Where-Object {$_.DatastoreIdentifier -eq $VMJournalDatastoreIdentifier} | select -ExpandProperty DatastoreName

$VMDatastoreName = $VIDatastoresCMD | Where-Object {$_.DatastoreIdentifier -eq $VMDatastoreIdentifier} | select -ExpandProperty DatastoreName

# Getting folder name

$VMFolderName = $VIFoldersCMD | Where-Object {$_.FolderIdentifier -eq $VMFolderIdentifier} | select -ExpandProperty FolderName

# Getting cluster name

$VMHostClusterName = $VIClustersCMD | Where-Object {$_.ClusterIdentifier -eq $VMHostClusterIdentifier} | select -ExpandProperty VirtualizationClusterName

# Getting host name

$VMHostName = $VIHostsCMD | Where-Object {$_.HostIdentifier -eq $VMHostIdentifier} | select -ExpandProperty VirtualizationHostName

# Getting resource pool name

$VMResourcePoolName = $VIResourcePoolsCMD | Where-Object {$_.ResourcePoolIdentifier -eq $VMResourcePoolIdentifier} | select -ExpandProperty ResourcepoolName

################################################

# Adding all VM setting info to $VMArray

################################################

$VMArrayLine = new-object PSObject

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value $VPGName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VPGidentifier" -Value $VPGidentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMName" -Value $VMName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMIdentifier" -Value $VMIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICCount" -Value $VMNICCount

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeCount" -Value $VMVolumeCount

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMProvisionedStorageInMB" -Value $VMProvisionedStorageInMB

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMUsedStorageInMB" -Value $VMUsedStorageInMB

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMBootGroupName" -Value $VMBootGroupName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMBootGroupDelay" -Value $VMBootGroupDelay

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMBootGroupIdentifier" -Value $VMBootGroupIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMJournalDatastoreClusterName" -Value $VMJournalDatastoreClusterName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMJournalDatastoreClusterIdentifier" -Value $VMJournalDatastoreClusterIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMJournalDatastoreName" -Value $VMJournalDatastoreName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMJournalDatastoreIdentifier" -Value $VMJournalDatastoreIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMJournalHardLimitInMB" -Value $VMJournalHardLimitInMB

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMJournalHardLimitInPercent" -Value $VMJournalHardLimitInPercent

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMDatastoreClusterName" -Value $VMDatastoreClusterName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMDatastoreClusterIdentifier" -Value $VMDatastoreClusterIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMDatastoreName" -Value $VMDatastoreName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMDatastoreIdentifier" -Value $VMDatastoreIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMFolderName" -Value $VMFolderName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMFolderIdentifier" -Value $VMFolderIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMHostClusterName" -Value $VMHostClusterName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMHostClusterIdentifier" -Value $VMHostClusterIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMHostName" -Value $VMHostName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMHostIdentifier" -Value $VMHostIdentifier

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMResourcePoolName" -Value $VMResourcePoolName

$VMArrayLine | Add-Member -MemberType NoteProperty -Name "VMResourcePoolIdentifier" -Value $VMResourcePoolIdentifier

$VMArray += $VMArrayLine

################################################

# Get VM Volume settings for the current VPG

################################################

$GetVMSettingVolumesURL = $baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/volumes"

$GetVMSettingVolumes = Invoke-RestMethod -Method Get -Uri $GetVMSettingVolumesURL -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

$GetVMSettingVolumeIDs = $GetVMSettingVolumes.ArrayOfVpgSettingsVmVolumeApi.VpgSettingsVmVolumeApi | select-object VolumeIdentifier -ExpandProperty VolumeIdentifier

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 24 OF 134

################################################

# Starting for each VM Volume ID action for collecting ZVM VM Volume data

################################################

foreach ($_ in $GetVMSettingVolumeIDs)

{

$VMVolumeID = $_

# Getting API data for volume

$GetVMSettingVolumeURL = $baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/volumes/"+$VMVolumeID

$GetVMSettingVolume = Invoke-RestMethod -Method Get -Uri $GetVMSettingVolumeURL -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

# Setting values

$VMVolumeDatastoreClusterIdentifier = $GetVMSettingVolume.VpgSettingsVmVolumeApi.Datastore.DatastoreClusterIdentifier

$VMVolumeDatastoreIdentifier = $GetVMSettingVolume.VpgSettingsVmVolumeApi.Datastore.DatastoreIdentifier

$VMVolumeIsSWAP = $GetVMSettingVolume.VpgSettingsVmVolumeApi.IsSwap

$VMVolumeIsThin = $GetVMSettingVolume.VpgSettingsVmVolumeApi.Datastore.IsThin

# Getting datastore cluster name

$VMVolumeDatastoreClusterName = $VIDatastoreClustersCMD | Where-Object {$_.DatastoreClusterIdentifier -eq $VMVolumeDatastoreClusterIdentifier} | select -ExpandProperty DatastoreClusterName

# Getting datastore name

$VMVolumeDatastoreName = $VIDatastoresCMD | Where-Object {$_.DatastoreIdentifier -eq $VMVolumeDatastoreIdentifier} | select -ExpandProperty DatastoreName

################################################

# Adding all VM Volume setting info to $VMVolumeArray

################################################

$VMVolumeArrayLine = new-object PSObject

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value $VPGName

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VPGidentifier" -Value $VPGidentifier

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMName" -Value $VMName

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMIdentifier" -Value $VMIdentifier

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeID" -Value $VMVolumeID

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeIsSWAP" -Value $VMVolumeIsSWAP

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeIsThin" -Value $VMVolumeIsThin

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeDatastoreClusterName" -Value $VMVolumeDatastoreClusterName

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeDatastoreClusterIdentifier" -Value $VMVolumeDatastoreClusterIdentifier

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeDatastoreName" -Value $VMVolumeDatastoreName

$VMVolumeArrayLine | Add-Member -MemberType NoteProperty -Name "VMVolumeDatastoreIdentifier" -Value $VMVolumeDatastoreIdentifier

$VMVolumeArray += $VMVolumeArrayLine

}

################################################

# Get VM Nic settings for the current VPG

################################################

$GetVMSettingNICsURL = $baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics"

$GetVMSettingNICs = Invoke-RestMethod -Method Get -Uri $GetVMSettingNICsURL -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

$VMNICIDs = $GetVMSettingNICs.ArrayOfVpgSettingsVmNicApi.VpgSettingsVmNicApi | select-object NicIdentifier -ExpandProperty NicIdentifier

################################################

# Starting for each VM NIC ID action for collecting ZVM VM NIC data

################################################

foreach ($_ in $VMNICIDs)

{

$VMNICIdentifier = $_

$GetVMSettingNICURL = $baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics/"+$VMNICIdentifier

$GetVMSettingNIC = Invoke-RestMethod -Method Get -Uri $GetVMSettingNICURL -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

# Building arrays

$VMSettingNICIDArray1 = $GetVMSettingNIC.VpgSettingsVmNicApi.Failover.Hypervisor

$VMSettingNICIDArray2 = $GetVMSettingNIC.VpgSettingsVmNicApi.Failover.Hypervisor.IpConfig

$VMSettingNICIDArray3 = $GetVMSettingNIC.VpgSettingsVmNicApi.FailoverTest.Hypervisor

$VMSettingNICIDArray4 = $GetVMSettingNIC.VpgSettingsVmNicApi.FailoverTest.Hypervisor.IpConfig

# Setting failover values

$VMNICFailoverDNSSuffix = $VMSettingNICIDArray1.DnsSuffix

$VMNICFailoverNetworkIdentifier = $VMSettingNICIDArray1.NetworkIdentifier

$VMNICFailoverShouldReplaceMacAddress = $VMSettingNICIDArray1.ShouldReplaceMacAddress

$VMNICFailoverGateway = $VMSettingNICIDArray2.Gateway

$VMNIsFailoverDHCP = $VMSettingNICIDArray2.IsDhcp

$VMNICFailoverPrimaryDns = $VMSettingNICIDArray2.PrimaryDns

$VMNICFailoverSecondaryDns = $VMSettingNICIDArray2.SecondaryDns

$VMNICFailoverStaticIp = $VMSettingNICIDArray2.StaticIp

$VMNICFailoverSubnetMask = $VMSettingNICIDArray2.SubnetMask

# Nulling blank content

if ($VMNICFailoverDNSSuffix.nil -eq $true){$VMNICFailoverDNSSuffix = $null}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 25 OF 134

if ($VMNICFailoverGateway.nil -eq $true){$VMNICFailoverGateway = $null}

if ($VMNICFailoverPrimaryDns.nil -eq $true){$VMNICFailoverPrimaryDns = $null}

if ($VMNICFailoverSecondaryDns.nil -eq $true){$VMNICFailoverSecondaryDns = $null}

if ($VMNICFailoverStaticIp.nil -eq $true){$VMNICFailoverStaticIp = $null}

if ($VMNICFailoverSubnetMask.nil -eq $true){$VMNICFailoverSubnetMask = $null}

# Setting failover test values

$VMNICFailoverTestDNSSuffix = $VMSettingNICIDArray3.DnsSuffix

$VMNICFailoverTestNetworkIdentifier = $VMSettingNICIDArray3.NetworkIdentifier

$VMNICFailoverTestShouldReplaceMacAddress = $VMSettingNICIDArray3.ShouldReplaceMacAddress

$VMNICFailoverTestGateway = $VMSettingNICIDArray4.Gateway

$VMNIsFailoverTestDHCP = $VMSettingNICIDArray4.IsDhcp

$VMNICFailoverTestPrimaryDns = $VMSettingNICIDArray4.PrimaryDns

$VMNICFailoverTestSecondaryDns = $VMSettingNICIDArray4.SecondaryDns

$VMNICFailoverTestStaticIp = $VMSettingNICIDArray4.StaticIp

$VMNICFailoverTestSubnetMask = $VMSettingNICIDArray4.SubnetMask

# Nulling blank content

if ($VMNICFailoverTestDNSSuffix.nil -eq $true){$VMNICFailoverTestDNSSuffix = $null}

if ($VMNICFailoverTestGateway.nil -eq $true){$VMNICFailoverTestGateway = $null}

if ($VMNICFailoverTestPrimaryDns.nil -eq $true){$VMNICFailoverTestPrimaryDns = $null}

if ($VMNICFailoverTestSecondaryDns.nil -eq $true){$VMNICFailoverTestSecondaryDns = $null}

if ($VMNICFailoverTestStaticIp.nil -eq $true){$VMNICFailoverTestStaticIp = $null}

if ($VMNICFailoverTestSubnetMask.nil -eq $true){$VMNICFailoverTestSubnetMask = $null}

# Mapping Network IDs to Names

$VMNICFailoverNetworkName = $VINetworksCMD | Where-Object {$_.NetworkIdentifier -eq $VMNICFailoverNetworkIdentifier} | Select VirtualizationNetworkName -ExpandProperty VirtualizationNetworkName

$VMNICFailoverTestNetworkName = $VINetworksCMD | Where-Object {$_.NetworkIdentifier -eq $VMNICFailoverTestNetworkIdentifier} | Select VirtualizationNetworkName -ExpandProperty VirtualizationNetworkName

################################################

# Adding all VM NIC setting info to $VMNICArray

################################################

$VMNICArrayLine = new-object PSObject

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value $VPGName

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VPGidentifier" -Value $VPGidentifier

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMName" -Value $VMName

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMIdentifier" -Value $VMIdentifier

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICIdentifier" -Value $VMNICIdentifier

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverNetworkName" -Value $VMNICFailoverNetworkName

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverNetworkIdentifier" -Value $VMNICFailoverNetworkIdentifier

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverDNSSuffix" -Value $VMNICFailoverDNSSuffix

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverShouldReplaceMacAddress" -Value $VMNICFailoverShouldReplaceMacAddress

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverGateway" -Value $VMNICFailoverGateway

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverDHCP" -Value $VMNIsFailoverDHCP

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverPrimaryDns" -Value $VMNICFailoverPrimaryDns

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverSecondaryDns" -Value $VMNICFailoverSecondaryDns

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverStaticIp" -Value $VMNICFailoverStaticIp

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverSubnetMask" -Value $VMNICFailoverSubnetMask

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestNetworkName" -Value $VMNICFailoverTestNetworkName

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestNetworkIdentifier" -Value $VMNICFailoverTestNetworkIdentifier

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestDNSSuffix" -Value $VMNICFailoverTestDNSSuffix

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestShouldReplaceMacAddress" -Value $VMNICFailoverTestShouldReplaceMacAddress

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestGateway" -Value $VMNICFailoverTestGateway

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestDHCP" -Value $VMNIsFailoverTestDHCP

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestPrimaryDns" -Value $VMNICFailoverTestPrimaryDns

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestSecondaryDns" -Value $VMNICFailoverTestSecondaryDns

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestStaticIp" -Value $VMNICFailoverTestStaticIp

$VMNICArrayLine | Add-Member -MemberType NoteProperty -Name "VMNICFailoverTestSubnetMask" -Value $VMNICFailoverTestSubnetMask

$VMNICArray += $VMNICArrayLine

# End of per VM NIC actions below

}

# End of per VM NIC actions above

#

# End of per VM actions below

}

# End of per VM actions above

################################################

# Deleting VPG edit settings ID (same as closing the edit screen on a VPG in the ZVM without making any changes)

################################################

Try

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 26 OF 134

{

Invoke-RestMethod -Method Delete -Uri $VPGSettingsURL -TimeoutSec 100 -Headers $zertoSessionHeader_XML -ContentType $TypeXML

}

Catch [system.exception]

{

}

#

# End of check for valid VPG settings ID below

}

# End of check for valid VPG settings ID above

#

# End of per VPG actions below

}

# End of per VPG actions above

#

################################################

# Showing Results - edit here for export commands etc

################################################

write-host "VPG Array:"

$VPGArray | fl

write-host "VM Array:"

$VMArray | fl

write-host "VM VOlume Array:"

$VMVolumeArray | fl

write-host "VM NIC Array:"

$VMNICArray | fl

# Exporting results

# Exporting arrays to CSV

$VPGArray | export-csv $ExportDataDir"VPGArray.csv" -NoTypeInformation

$VMArray | export-csv $ExportDataDir"VMArray.csv" -NoTypeInformation

$VMVolumeArray | export-csv $ExportDataDir"VMVolumeArray.csv" -NoTypeInformation

$VMNICArray | export-csv $ExportDataDir"VMNICArray.csv" -NoTypeInformation

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 27 OF 134

4 DAILY EMAIL REPORTS

4.1 Use Cases

Utilizing the examples from section 3.0 makes it possible to build daily email reports of the vital information consolidated from multiple Zerto installations. These email reports can contain data from both the Zerto Virtual Manager (ZVM) and vCenter to give a complete view of the data required. Some example examples include:

• Summary of protected and unprotected VMs and VPGs across multiple vCenters and ZVMs • VPG report with current RPO and status • Protected VM report with current RPO and status • Unprotected VM report with configuration statistics • Target VRA load • Target Datastore load • All VPG and VM settings

4.2 Design Methodology

To support reporting across multiple vCenters and ZVMs, each combination is referred to as a POD in the example script. I.E Site1 vCenter and ZVM is ZVRPRODPOD1 and Site 2 is ZVRDRPOD1, this is repeated twice to show the ability to report on multiple PODs and should be configured to your own environment settings.

4.3 Daily Email Report

Following is a complete example script with comments on configuration required:

#################################################Description:#ThisscriptautomatesthecreationofmultiplereportsacrossmanyZertoVirtualManagersandvCenters#################################################Requirements:#1.RunPowerShellasadministratorwithcommand"Set-ExecutionPolcityunrestricted"#2.VerifyscriptserverhasconnectivitytoallvCenters,ZVMsandanSMTPserver#3.InstallVMwarePowerCLI6.0#4.Configurevariablesin“SetCredentialsforallvCenters&ZVMs”,“SMTPEmailProfileSettings”andset“CreatingvCenter&ZVMMappingstoreporton”#5.CredentialsconfiguredhavereadaccesstovCenterandview/editVPGpermissionsinZVM#6.Tostorecredentialssecurelyconsiderusing:#https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Encryption-45709b87#7.ThepasswordneedstobedecryptedforusebytheZertoAPIwhichsendsitoverHTTPstokeepitsecure#8.Addadditionalemaillistsifrequiredandincrementthenumber#9.EachcombinationofavCentersandZVMisreferredtoasaPOD,addyourPODsintotheCreate-vCenterArrayfunctiononline24andremoveeisting#10.Startwithdefaultreportsandcustomizefromline2500#11.Runthescriptmanuallyorscheduleusingwindowstaskscheduler#12.RecommendedtorunforthefirsttimeinPowerShellISEfortroubleshooting#13.Thescriptisn’tconfiguredtousetranscriptionforloggingofexceptions#################################################SetCredentialsforallvCenters&ZVMs################################################$Username="[email protected]"$Password="Zerto1234!"$CSVDirectory="C:\ZVRReports\"#ConfiguretargetZVMresourcereportsamplingrate,bydefaultdaily,ifleftasdailythensetthebelowtofalseandallZVMsshouldbeconfiguredtothesamesetting$ResourceReportHourlySample="TRUE"#################################################SMTPEmailProfileSettings################################################$EmailList1="[email protected]"$EmailFrom="[email protected]"$SMTPServer="localhost"$SMTPPort="25"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 28 OF 134

$SMTPUser="username"$SMTPPassword="password"$SMTPSSLEnabled="FALSE"#CreatingSMTPProfiles$SMTPProfile1=@("$EmailFrom",“$SMTPServer”,”$SMTPPort”,”$SMTPUser”,”$SMTPPassword”,”$SMTPSSLEnabled”)#################################################CreatingvCenter&ZVMMappingstoreporton-changethistomatchenvironment################################################FunctionCreate-vCenterArray{$vCenterArray=@()#ZertoDemoLAB1$vCenterArrayLine=new-objectPSObject$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value"ZVRPRODPOD1"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcevCenter"-Value"192.168.0.81"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourceZVM"-Value"192.168.0.31"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetPOD"-Value"ZVRDRPOD1"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetvCenter"-Value"192.168.0.82"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetZVM"-Value"192.168.0.32"$vCenterArray+=$vCenterArrayLine#ZertoDemoLAB2$vCenterArrayLine=new-objectPSObject$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value"ZVRPRODPOD2"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcevCenter"-Value"192.168.0.81"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourceZVM"-Value"192.168.0.31"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetPOD"-Value"ZVRDRPOD2"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetvCenter"-Value"192.168.0.82"$vCenterArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetZVM"-Value"192.168.0.32"$vCenterArray+=$vCenterArrayLine#Outputtingarraytofunction$vCenterArray}#Runningfunctioncreated$vCenterArray=Create-vCenterArray#################################################################################################################################################Nothingtoconfigurebelowthislinetoreceivethedefaultreports#################################################################################################################################################################################################BuildingHTMLsettingsforEmailreports################################################$TableFont="#FFFFFF"$TableBackground="#B20000"$TableBorder="#e60000"$ReportHTMLTableStyle=@"<styletype="text/css">.tg{border-collapse:collapse;border-spacing:0;border-color:#aaa;}

.tgtd{font-family:Arial,sans-serif;font-size:10px;padding:10px5px;border-style:solid;border-width:0px;overflow:hidden;word-break:normal;border-color:#aaa;color:#333;background-color:#ffffff;border-top-width:1px;border-bottom-width:1px;}

.tgth{font-family:Arial,sans-serif;font-size:10px;font-weight:bold;padding:10px5px;border-style:solid;border-width:0px;overflow:hidden;word-break:normal;border-color:#aaa;color:$TableFont;background-color:$TableBorder;border-top-width:1px;border-bottom-width:1px;}

.tg.tg-foxd{background-color:$TableBackground;vertical-align:top;text-align:left}

.tg.tg-yw4l{vertical-align:top}

.caption{font-family:Arial,sans-serif;font-size:11px;font-weight:bold;color:$TableFont;}

</style>"@#################################################CreatingCSVSaveFunction################################################FunctionSave-CSV{Param($Array,$CSVFileName,$CSVDirectory)#Savingfiletodirectoryspecifiedthenreturningfilenametouseforemail$Timestamp=get-date$Now=$TimeStamp.ToString("yyyy-MM-ddHH-mm-ss")$CSVName=$Now+$CSVFileName$CSVFile=$CSVDirectory+$CSVName+".csv"$Array|Export-CSV-NoTypeInformation$CSVFile$CSVFile}################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 29 OF 134

#CreatingTimefunction################################################FunctionGet-Time{$Timestamp=get-date$Now=$TimeStamp.ToString("yyyy-MM-ddHH-mm-ss")$Now}#################################################CreatingEmailFunction################################################FunctionEmail-ZVRReport{Param($EmailTo,$Subject,$Body,$Attachment,$SMTPProfile)#GettingSMTPProfileSettings$EmailFrom=$SMTPProfile[0]$SMTPServer=$SMTPProfile[1]$SMTPPort=$SMTPProfile[2]$SMTPUser=$SMTPProfile[3]$SMTPPassword=$SMTPProfile[4]$SMTPSSLEnabled=$SMTPProfile[5]#BuildingSMTPsettingsbasedonsettings$emailsetting=New-ObjectSystem.Net.Mail.MailMessage$emailsetting.to.add($EmailTo)$emailsetting.from=$EmailFrom$emailsetting.IsBodyHTML="TRUE"$emailsetting.subject=$Subject$emailsetting.body=$Body#Addingattachmentsif($Attachment-ne$null){#Performingforeachtosupportmultipleattachmentsforeach($_in$Attachment){$emailattachmentsetting=new-objectSystem.Net.Mail.Attachment$_$emailsetting.attachments.add($emailattachmentsetting)#invoke-expression$AttachmentCommand#Endofforeachattachmentbelow}#Endofforeachattachmentabove}#CreatingSMTPobject$smtp=New-ObjectSystem.Net.Mail.SmtpClient($SMTPServer,$SMTPPort);#EnablingSSLifsetif($SMTPSSLEnabled-eq"TRUE"){$smtp.EnableSSL="TRUE"}#Settingcredentials$smtp.Credentials=New-ObjectSystem.Net.NetworkCredential($SMTPUser,$SMTPPassword);#SendingtheEmailTry{$smtp.send($emailsetting)}Catch[system.exception]{#Tryingemailagain$smtp.send($emailsetting)}#Endofemailfunction}#################################################CreatingReportarrays################################################$ProtectedVPGArray=@()$ProtectedVMArray=@()$TargetVRAArray=@()

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 30 OF 134

$UnprotectedVMArray=@()$TargetDatastoreArray=@()$VPGArray=@()$VMArray=@()$VMVolumeArray=@()$VMNICArray=@()$PODSummaryArray=@()#################################################CreatingVmStatusArray################################################FunctionCreate-VMStatusArray{$VMStatusArray=@()#Status0$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"0"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"Initializing"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisbeinginitialized.ThisincludeswhenaVPGiscreatedandduringinitialsync."$VMStatusArray+=$VMStatusArrayLine#Status1$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"1"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"MeetingSLA"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGismeetingtheSLAspecification."$VMStatusArray+=$VMStatusArrayLine#Status2$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"2"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"NotMeetingSLA"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisnotmeetingtheSLAforboththejournalhistoryandRPOSLAsettings."$VMStatusArray+=$VMStatusArrayLine#Status3$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"3"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"HistoryNotMeetingSLA"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisnotmeetingtheSLAspecificationforthejournalhistory."$VMStatusArray+=$VMStatusArrayLine#Status4$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"4"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"RpoNotMeetingSLA"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisnotmeetingtheSLAspecificationfortheRPOSLAsetting."$VMStatusArray+=$VMStatusArrayLine#Status5$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"5"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"FailingOver"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisinaFailoveroperation."$VMStatusArray+=$VMStatusArrayLine#Status6$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"6"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"Moving"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisinaMoveoperation."$VMStatusArray+=$VMStatusArrayLine#Status7$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"7"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"Deleting"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGisbeingdeleted."$VMStatusArray+=$VMStatusArrayLine#Status8$VMStatusArrayLine=new-objectPSObject$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"8"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"Recovered"$VMStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"TheVPGhasbeenrecovered."$VMStatusArray+=$VMStatusArrayLine

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 31 OF 134

#Outputtingarraytofunction$VMStatusArray}#Runningfunctioncreated$VMStatusArray=Create-VMStatusArray#################################################CreatingEventStatusArray################################################FunctionCreate-EventStatusArray{$EventStatusArray=@()#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"14"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"STR0001"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Datastorenotaccessible"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"15"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"STR0002"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Datastoreisfull"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"16"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"STR0004"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Datastorelowinspace"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"32"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0003"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGhaslowjournalhistory"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"33"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0004"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGhaslowjournalhistory"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"34"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0005"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGinerrorstate"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"35"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0006"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGmissingconfigurationdetails"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"36"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0007"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGreplicationpaused"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"37"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0008"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrollbackfailed"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 32 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"38"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0009"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGtargetRPOexceeded"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"39"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0010"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGtargetRPOexceeded"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"40"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0011"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGtestoverdue"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"41"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0012"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGtestoverdue"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"42"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0014"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGwaitingforcommitorrollback"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"43"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0015"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"ResourcesnotenoughtosupportVPG"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"44"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0016"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Resourcespoolnotfound"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"45"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0017"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGprotectionpaused"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"46"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0018"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VMsinVPGnotconfiguredwithastorageprofile"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"47"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0019"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofiledisabled"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"48"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0020"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofilenotfound"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 33 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"49"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0021"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofilenotfound"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"50"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0022"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofiledisabled"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"51"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0023"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofilenotfound"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"52"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0024"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofiledoesnotincludeactivedatastores"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"53"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0025"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"vAppnetworkmappingnotdefined"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"54"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0026"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoverystorageprofilechanged"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"55"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0027"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGincludesVMsthatarenolongerprotected"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"56"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0028"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"CorruptedOrgvDCnetworkmapping"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"57"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0035"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGprotectedresourcesnotinZORG"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"58"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0036"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VPGrecoveryresourcesnotinZORG"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"59"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0037"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Journalhistoryiscompromised"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 34 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"60"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0038"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Journalhistoryiscompromised"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"61"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0039"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"RDMhasanoddnumberofblocks"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"62"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0040"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Virtualmachinehardwaremismatchwithrecoverysite"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"63"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0041"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VirtualmachinerunningWindows2003"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"64"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0042"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Recoverynetworknotfound"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"65"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VPG0043"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Cross-replication"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"66"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0001"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"HostwithoutVRA"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"67"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0002"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAwithoutIP"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"68"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0003"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"HostIPchanges"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"69"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0004"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAlostIP"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"70"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0005"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAsnotconnected"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 35 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"71"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0006"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Datastoreforjournaldiskisfull"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"72"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0007"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"I/Oerrortojournal"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"73"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0008"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"RecoverydiskandVMsmissing"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"74"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0009"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Recoverydiskmissing"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"75"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0010"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Recoverydisksturnedoff"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"76"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0011"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Recoverydiskinaccessible"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"77"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0012"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Cannotwritetorecoverydisk"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"78"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0013"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"I/Oerrortorecoverydisk"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"79"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0014"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Cloneddisksturnedoff"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"80"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0015"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Cloneddiskinaccessible"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"81"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0016"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Datastoreforclonediskisfull"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 36 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"82"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0017"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"I/Oerrortoclone"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"83"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0018"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"ProtecteddiskandVMmissing"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"84"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0019"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Protecteddiskmissing"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"85"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0020"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VMpoweredoff"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"86"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0021"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VMdiskinaccessible"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"87"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0022"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VMdiskincompatible"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"88"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0023"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAcannotberegistered"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"89"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0024"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAremoved"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"90"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0025"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"I/Osynchronization"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"91"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0026"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Recoverydiskremoved"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"92"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0027"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Journaldiskremoved"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 37 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"93"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0028"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRApoweredoff"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"94"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0029"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAmemorylow"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"95"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0030"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Journalsizemismatch"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"96"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0032"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAout-of-date"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"97"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0035"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"VRAreconciliation"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"98"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0037"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"LocalMACAddressConflict"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"99"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0038"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"MACAddressConflict"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"100"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0039"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Journalreachedconfiguredlimit"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"101"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0040"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Journalspacelow"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"102"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0049"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Hostrollbackfailed"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"103"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"VRA0050"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Wronghostpassword"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 38 OF 134

$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"108"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0001"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Noconnectiontohypervisormanager"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"109"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0002"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"NoconnectiontoVRA"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"110"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0003"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Noconnectiontosite"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"111"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0004"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Peersiteout-of-date"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"112"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0005"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"ZertoVirtualManagerspacelow"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"113"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0006"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Upgradeavailable"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"114"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0007"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Cannotupgrade"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"115"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0008"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Versionmismatch"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"116"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0009"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"Internalerror"$EventStatusArray+=$EventStatusArrayLine#Statusline$EventStatusArrayLine=new-objectPSObject$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"117"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Identifier"-Value"ZVM0010"$EventStatusArrayLine|Add-Member-MemberTypeNoteProperty-Name"Description"-Value"SynchronizationbetweenZertoVirtualManagers"$EventStatusArray+=$EventStatusArrayLine#Outputting$EventStatusArray}#Runningfunctioncreated$EventStatusArray=Create-EventStatusArray#################################################CreatingPriorityArray################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 39 OF 134

FunctionCreate-VMPriorityArray{$VMPriorityArray=@()#Priority1$VMPriorityArrayLine=new-objectPSObject$VMPriorityArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"1"$VMPriorityArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"High"$VMPriorityArray+=$VMPriorityArrayLine#Priority2$VMPriorityArrayLine=new-objectPSObject$VMPriorityArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"2"$VMPriorityArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"Medium"$VMPriorityArray+=$VMPriorityArrayLine#Priority3$VMPriorityArrayLine=new-objectPSObject$VMPriorityArrayLine|Add-Member-MemberTypeNoteProperty-Name"Number"-Value"3"$VMPriorityArrayLine|Add-Member-MemberTypeNoteProperty-Name"Name"-Value"Low"$VMPriorityArray+=$VMPriorityArrayLine#Outputting$VMPriorityArray}$VMPriorityArray=Create-VMPriorityArray#################################################BuildingReportsperPOD################################################foreach($PODin$vCenterArray){#Settingvariables$SourcePOD=$POD.SourcePOD$SourcevCenter=$POD.SourcevCenter$SourceZVM=$POD.SourceZVM$TargetPOD=$POD.TargetPOD$TargetvCenter=$POD.TargetvCenter$TargetZVM=$POD.TargetZVM#################################################ConnectingtosourcevCenterforsourceVMinfo################################################Try{write-host"ConnectingtovCenter:$SourcevCenter"connect-viserver-Server$SourcevCenter-User$Username-Password$Password$SourcevCenterAuthentication="PASS"}Catch{$SourcevCenterAuthentication="FAIL"}#ConnectingtoTargetvCenterfortargetinfoTry{connect-viserver-Server$TargetvCenter-User$Username-Password$Password$TargetvCenterAuthentication="PASS"}Catch{$TargetvCenterAuthentication="FAIL"}#CatchingfailedvCenterauthentication,onlyrunningreportsforPODifitpasssesif(($TargetvCenterAuthentication-eq"PASS")-and($SourcevCenterAuthentication-eq"PASS")){#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeSourceZertoAPI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 40 OF 134

WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingSourceZertoAPIstringandinvokingAPI################################################$SourceZVMBaseURL="https://"+$SourceZVM+":"+"9669"+"/v1/"#AuthenticatingwithZertoAPIs$SourceZVMSessionURL=$SourceZVMBaseURL+"session/add"$SourceZVMAuthInfo=("{0}:{1}"-f$Username,$Password)$SourceZVMAuthInfo=[System.Text.Encoding]::UTF8.GetBytes($SourceZVMAuthInfo)$SourceZVMAuthInfo=[System.Convert]::ToBase64String($SourceZVMAuthInfo)$SourceZVMHeaders=@{Authorization=("Basic{0}"-f$SourceZVMAuthInfo)}$SourceZVMSessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"Try{$SourceZVMSessionResponse=Invoke-WebRequest-Uri$SourceZVMSessionURL-Headers$SourceZVMHeaders-MethodPOST-Body$SourceZVMSessionBody-ContentType$TypeJSON$SourceZVMAuthentication="PASS"}Catch{$SourceZVMAuthentication="FAIL"}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$SourceZVMSession=$SourceZVMSessionResponse.headers.get_item("x-zerto-session")$SourceZVMSessionHeader_JSON=@{"x-zerto-session"=$SourceZVMSession;"Accept"=$TypeJSON}$SourceZVMSessionHeader_XML=@{"x-zerto-session"=$SourceZVMSession;"Accept"=$TypeXML}if($SourceZVMAuthentication-eq"PASS"){#GetSiteIdentifierforlaterinthescript$SourceSiteInfoURL=$SourceZVMBaseURL+"localsite"$SourceSiteInfoCMD=Invoke-RestMethod-Uri$SourceSiteInfoURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON$SourceLocalSiteIdentifier=$SourceSiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier}#################################################SettingCertPolicyfor2ndAPIcall-requiredforsuccessfulauthwithTargetZertoAPI-hastoberunagain################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingTaretZertoAPIstringandinvokingAPI################################################$TargetZVMBaseURL="https://"+$TargetZVM+":"+"9669"+"/v1/"$TargetZVMBaseResourceReportURL="https://"+$TargetZVM+":"+"9669"#AuthenticatingwithZertoAPIs$TargetZVMSessionURL=$TargetZVMBaseURL+"session/add"$TargetZVMAuthInfo=("{0}:{1}"-f$Username,$Password)$TargetZVMAuthInfo=[System.Text.Encoding]::UTF8.GetBytes($TargetZVMAuthInfo)$TargetZVMAuthInfo=[System.Convert]::ToBase64String($TargetZVMAuthInfo)$TargetZVMHeaders=@{Authorization=("Basic{0}"-f$TargetZVMAuthInfo)}$TargetZVMSessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 41 OF 134

$TypeXML="application/xml"Try{$TargetZVMSessionResponse=Invoke-WebRequest-Uri$TargetZVMSessionURL-Headers$TargetZVMHeaders-MethodPOST-Body$TargetZVMSessionBody-ContentType$TypeJSON$TargetZVMAuthentication="PASS"}Catch{$TargetZVMAuthentication="FAIL"}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$TargetZVMSession=$TargetZVMSessionResponse.headers.get_item("x-zerto-session")$TargetZVMSessionHeader=@{"x-zerto-session"=$TargetZVMSession;"Accept"=$TypeJSON}if($TargetZVMAuthentication-eq"PASS"){#GetSiteIdentifierforlaterinthescript$TargetSiteInfoURL=$TargetZVMBaseURL+"localsite"$TargetSiteInfoCMD=Invoke-RestMethod-Uri$TargetSiteInfoURL-TimeoutSec100-Headers$TargetZVMSessionHeader-ContentType$TypeJSON$TargetLocalSiteIdentifier=$TargetSiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier}#################################################Gettinglastresourcereportsample,foruseinemailreports################################################if($ResourceReportHourlySample-eq"TRUE"){$NowDateTime=get-date-Format"yyyy-MM-ddHH:mm:ss"$ThenDateTime=(get-date).AddHours(-1).ToString("yyyy-MM-ddHH:mm:ss")}else{$StartDateTime=get-date-Format"yyyy-MM-dd"$EndDateTime=(get-date).AddDays(1).ToString("yyyy-MM-dd")}#QueryResourceReportwithentriesfromthelasthour$ResourceReprtString="/ZvmService/ResourcesReport/getSamples?fromTimeString="$ResourceReportURL=$TargetZVMBaseResourceReportURL+$ResourceReprtString+$ThenDateTime+"&toTimeString="+$NowDateTime+"&startIndex=0&count=500"$ResourceReport=Invoke-RestMethod-Uri$ResourceReportURL-TimeoutSec100-Headers$TargetZVMSessionHeader-ContentType$TypeJSON#################################################CreatingProtectedVPGArray#################################################GettingVPGs$ProtectedVPGsURL=$SourceZVMBaseURL+"vpgs"$ProtectedVPGsCMD=Invoke-RestMethod-Uri$ProtectedVPGsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSONforeach($VPGin$ProtectedVPGsCMD){$VPGName=$VPG.VpgName$VPGIdentifier=$VPG.VpgIdentifier$VMCount=$VPG.VmsCount$PriorityNumber=$VPG.Priority$RPO=$VPG.ActualRPO$StatusNumber=$VPG.Status$SizeInGb=$VPG.UsedStorageInMB/1024$SizeInGb=[math]::Round($SizeInGb,2)#Convertingpriority$VPGPriority=$VMPriorityArray|Where-Object{$_.Number-eq$PriorityNumber}|select-ExpandPropertyName#ConvertingVMstatus$VPGStatus=$VMStatusArray|Where-Object{$_.Number-eq$StatusNumber}|select-ExpandPropertyName$VPGStatusDescription=$VMStatusArray|Where-Object{$_.Number-eq$StatusNumber}|select-ExpandPropertyDescription#GettingVPGJournalsize$VPGResourceReport=$ResourceReport|Where-Object{$_.VpgName-eq$VPGName}#CalculatingtotalJournalusage$VPGJournalUsage=$VPGResourceReport.RecoveryJournalUsedStorageInGB$VPGTotalJournalUsage=0foreach($_in$VPGJournalUsage){$VPGTotalJournalUsage+=$_}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 42 OF 134

$VPGTotalJournalUsage=[math]::Round($VPGTotalJournalUsage,2)#GettingAlertsfortheVPGforpast24hours$Tomorrow=(get-date).AddDays(1)$Yesterday=(get-date).AddDays(-1)#BuildingURL$VPGAlertsURL=$SourceZVMBaseURL+"alerts?"+"startDate=$Yesterday&endDate=$Tomorrow&vpgIdentifier={$VPGIdentifier}&isDismissed=false"#Gettingevents$VPGAlertsCMD=Invoke-RestMethod-Uri$VPGAlertsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON$VPGLastAlert=$VPGAlertsCMD|select*-First1#Gettingdescriptionoflastalert$VPGLastAlertIdentifier=$VPGLastAlert.HelpIdentifier$VPGLastAlertDescription=$EventStatusArray|Where-Object{$_.Identifier-eq$VPGLastAlertIdentifier}|select-expandpropertyDescription#CalculatingRPOviolationsinlast24hours$VPGRPOAlerts=$VPGAlertsCMD|Where-Object{$_.HelpIdentifier-eq"VPG0009"-or$_.HelpIdentifier-eq"VPG0009"}|Measure-Object|select-ExpandPropertyCount#Addingtoarray$ProtectedVPGArrayLine=new-objectPSObject$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value$SourcePOD$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetPOD"-Value$TargetPOD$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCount"-Value$VMCount$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"Priority"-Value$VPGPriority$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"RPO"-Value$RPO$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"RPOAlerts"-Value$VPGRPOAlerts$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"Status"-Value$VPGStatus$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"SizeInGb"-Value$SizeInGb$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"JournalSizeInGb"-Value$VPGTotalJournalUsage$ProtectedVPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"AlertDescription"-Value$VPGLastAlertDescription$ProtectedVPGArray+=$ProtectedVPGArrayLine}#GettingVMs$ProtectedVMsURL=$SourceZVMBaseURL+"vms"$ProtectedVMsCMD=Invoke-RestMethod-Uri$ProtectedVMsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Addingtoarray$ProtectedVMs=$ProtectedVMsCMD|Sort-ObjectVpgNameforeach($VMin$ProtectedVMs){$VPGName=$VM.VpgName$VMName=$VM.VmName$StatusNumber=$VM.Status$PriorityNumber=$VM.Priority$RPO=$VM.ActualRPO$SizeInGb=$VM.UsedStorageInMB/1024$SizeInGb=[math]::Round($SizeInGb,2)$VMDisks=$VM.Volumes.Count#Convertingpriority$VMPriority=$VMPriorityArray|Where-Object{$_.Number-eq$PriorityNumber}|select-ExpandPropertyName#ConvertingVMstatus$VMStatus=$VMStatusArray|Where-Object{$_.Number-eq$StatusNumber}|select-ExpandPropertyName$VMStatusDescription=$VMStatusArray|Where-Object{$_.Number-eq$StatusNumber}|select-ExpandPropertyDescription#GettongVMJournalsize$VMResourceReport=$ResourceReport|Where-Object{$_.VmName-eq$VMName}|select-First1$VMSourceCluster=$VMResourceReport.SourceCluster$VMTargetCluster=$VMResourceReport.TargetCluster#CalculatingtotalJournalusage$VMJournalUsage=$VMResourceReport.RecoveryJournalUsedStorageInGB$VMTotalJournalUsage=0foreach($_in$VMJournalUsage){$VMTotalJournalUsage+=$_}$VMTotalJournalUsage=[math]::Round($VMTotalJournalUsage,2)#Creatingarrayline$ProtectedVMArrayLine=new-objectPSObject$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value"$SourcePOD"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourceCluster"-Value"$VMSourceCluster"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetPOD"-Value"$TargetPOD"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 43 OF 134

$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetCluster"-Value"$VMTargetCluster"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value"$VPGName"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value"$VMName"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"Priority"-Value"$VMPriority"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"RPO"-Value"$RPO"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"Status"-Value"$VMStatus"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"Disks"-Value"$VMDisks"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"SizeInGb"-Value"$SizeInGb"$ProtectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"JournalSizeInGb"-Value"$VMTotalJournalUsage"$ProtectedVMArray+=$ProtectedVMArrayLine}#################################################CreatingTargetVRAArray################################################$TargetZVMHostsURL=$TargetZVMBaseURL+"virtualizationsites/"+$TargetLocalSiteIdentifier+"/hosts"$TargetZVMHostsCMD=Invoke-RestMethod-Uri$TargetZVMHostsURL-TimeoutSec100-Headers$TargetZVMSessionHeader-ContentType$TypeJSON$TargetZVMVRAsURL=$TargetZVMBaseURL+"vras"$TargetZVMVRAsCMD=Invoke-RestMethod-Uri$TargetZVMVRAsURL-TimeoutSec100-Headers$TargetZVMSessionHeader-ContentType$TypeJSON$TargetZVMVRAs=$TargetZVMVRAsCMD|Select-ObjectVraName,HostIdentifier,VraGroup,RecoveryCounters-Unique#ForeachVRAforeach($TargetVRAin$TargetZVMVRAs){$VRAName=$TargetVRA.VraName$VRACluster=get-vm$VRAName|Get-Cluster|select-expandpropertyName$VRAHostIdentifier=$TargetVRA.HostIdentifier$VRAVMs=$TargetVRA.RecoveryCounters.Vms$VRAHostIdentifier=$TargetVRA.HostIdentifier$VRAVolumes=$TargetVRA.RecoveryCounters.Volumes$VRAVpgs=$TargetVRA.RecoveryCounters.Vpgs$VRAGroup=$TargetVRA.VraGroup#Gettinghostname$VRAHostname=$TargetZVMHostsCMD|Where-Object{$_.HostIdentifier-eq$VRAHostIdentifier}|select-ExpandPropertyVirtualizationHostName#Gettingovercommitdatafromresourcereport$TargetVraData=$ResourceReport|Where-Object{$_.TargetVraName-eq$VRAName}|Select-ObjectNumberOfvCpu,CpuUsedInMhz,MemoryInMB,ActiveGuestMemoryInMB,BandwidthInBytes,RecoveryVolumesUsedStorageInGB,RecoveryJournalUsedStorageInGB

#CalculatingtotalCPUs$TargetVraNumberOfvCPU=$TargetVraData.NumberOfvCpu$TotalTargetVraNumberOfvCPU=0foreach($_in$TargetVraNumberOfvCPU){$TotalTargetVraNumberOfvCPU+=$_}#CalculatingtotalCPUmhz$TargetVraCpuUsedInMhz=$TargetVraData.CpuUsedInMhz$TotalTargetVraCpuUsedInMhz=0foreach($_in$TargetVraCpuUsedInMhz){$TotalTargetVraCpuUsedInMhz+=$_}$TotalTargetVraCpuUsedInGhz=$TotalTargetVraCpuUsedInMhz/1000$TotalTargetVraCpuUsedInGhz=[math]::Round($TotalTargetVraCpuUsedInGhz,2)#CalculatingtotalMemoryInMB$TargetVraMemoryInMB=$TargetVraData.MemoryInMB$TotalTargetVraMemoryInMB=0foreach($_in$TargetVraMemoryInMB){$TotalTargetVraMemoryInMB+=$_}$TotalTargetVraMemoryInGB=$TotalTargetVraMemoryInMB/1024$TotalTargetVraMemoryInGB=[math]::Round($TotalTargetVraMemoryInGB,2)#CalculatingtotalActiveGuestMemoryInMB$TargetVraActiveGuestMemoryInMB=$TargetVraData.ActiveGuestMemoryInMB$TotalTargetVraActiveGuestMemoryInMB=0foreach($_in$TargetVraActiveGuestMemoryInMB){$TotalTargetVraActiveGuestMemoryInMB+=$_}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 44 OF 134

$TotalTargetVraActiveGuestMemoryInGB=$TotalTargetVraActiveGuestMemoryInMB/1024$TotalTargetVraActiveGuestMemoryInGB=[math]::Round($TotalTargetVraActiveGuestMemoryInGB,2)#CalculatingtotalBandwidthInBytes$TargetVraBandwidthInBytes=$TargetVraData.BandwidthInBytes$TotalTargetVraBandwidthInBytes=0foreach($_in$TargetVraBandwidthInBytes){$TotalTargetVraBandwidthInBytes+=$_}#CalculatingtotalRecoveryVolumesUsedStorageInGB&TB$TargetVraRecoveryVolumesUsedStorageInGB=$TargetVraData.RecoveryVolumesUsedStorageInGB$TotalTargetVraRecoveryVolumesUsedStorageInGB=0foreach($_in$TargetVraRecoveryVolumesUsedStorageInGB){$TotalTargetVraRecoveryVolumesUsedStorageInGB+=$_}$TotalTargetVraRecoveryVolumesUsedStorageInTB=$TotalTargetVraRecoveryVolumesUsedStorageInGB/1024$TotalTargetVraRecoveryVolumesUsedStorageInTB=[math]::Round($TotalTargetVraRecoveryVolumesUsedStorageInTB,2)#CalculatingtotalRecoveryJournalUsedStorageInGB$TargetVraRecoveryJournalUsedStorageInGB=$TargetVraData.RecoveryJournalUsedStorageInGB$TotalTargetVraRecoveryJournalUsedStorageInGB=0foreach($_in$TargetVraRecoveryJournalUsedStorageInGB){$TotalTargetVraRecoveryJournalUsedStorageInGB+=$_}$TotalTargetVraRecoveryJournalUsedStorageInTB=$TotalTargetVraRecoveryJournalUsedStorageInGB/1024$TotalTargetVraRecoveryJournalUsedStorageInTB=[math]::Round($TotalTargetVraRecoveryJournalUsedStorageInTB,2)#Creatingarray$TargetVRAArrayLine=new-objectPSObject$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetPOD"-Value$TargetPOD$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRACluster"-Value$VRACluster$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRAName"-Value$VRAName$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"ESXiHostname"-Value$VRAHostname$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRAVPGs"-Value$VRAVpgs$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRAVMs"-Value$VRAVMs$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRAVolumes"-Value$VRAVolumes$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRABandwidthInBytes"-Value$TotalTargetVraBandwidthInBytes$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRARecoveryVolumesInGB"-Value$TotalTargetVraRecoveryVolumesUsedStorageInGB$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VraRecoveryVolumesInTB"-Value$TotalTargetVraRecoveryVolumesUsedStorageInTB$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRARecoveryJournalsInGB"-Value$TotalTargetVraRecoveryJournalUsedStorageInGB$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRARecoveryJournalsInTB"-Value$TotalTargetVraRecoveryJournalUsedStorageInTB$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNumberOfvCPU"-Value$TotalTargetVraNumberOfvCPU$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCpuUsedInGhz"-Value$TotalTargetVraCpuUsedInGhz$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMMemoryInGB"-Value$TotalTargetVraMemoryInGB$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMActiveMemoryInGB"-Value$TotalTargetVraActiveGuestMemoryInGB$TargetVRAArray+=$TargetVRAArrayLine}#################################################CreatingUnprotectedVMArray#################################################UsingZVRAPItogetVMs$SourceZVMUnprotectedVMsURL=$SourceZVMBaseURL+"virtualizationsites/"+$SourceLocalSiteIdentifier+"/vms"$SourceZVMUnprotectedVMsCMD=Invoke-RestMethod-Uri$SourceZVMUnprotectedVMsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#ForeachunprotectedVMforeach($VMin$SourceZVMUnprotectedVMsCMD){#GettingvCenterVMIDfromZVRID$VMName=$VM.VmName$VMZVRID=$VM.VmIdentifier$Separator="."$VMZVRIDSplit=$VMZVRID.split($Separator)$VMID=$VMZVRIDSplit[1]$VMID="VirtualMachine-"+$VMID#UsingvCenterVMIDtogetmoreinfo#Gettingclusterinfo$VMCluster=get-vm-Id$VMID|Get-Cluster|select-expandpropertyName-First1

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 45 OF 134

$VMInfo=get-vm-Id$VMID|selectFolder,NumCPU,MemoryGB,HardDisks,NetworkAdapters,UsedSpaceGB-First1$VMFolder=$VMInfo.Folder$VMNumCPU=$VMInfo.NumCpu$VMMemoryGB=$VMInfo.MemoryGB$VMMemoryGB=[math]::Round($VMMemoryGB,2)$VMHardDisks=$VMInfo.HardDisks.Count$VMNICS=$VMInfo.NetworkAdapters.Count$VMUsedSpaceGB=$VMInfo.UsedSpaceGB$VMUsedSpaceGB=[math]::Round($VMUsedSpaceGB,2)#Buildingarrayline$UnprotectedVMArrayLine=new-objectPSObject$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value"$SourcePOD"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMFolder"-Value"$VMFolder"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value"$VMName"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCluster"-Value"$VMCluster"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"NumCPU"-Value"$VMNumCPU"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"MemoryGB"-Value"$VMMemoryGB"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"NICS"-Value"$VMNICS"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"HardDisks"-Value"$VMHardDisks"$UnprotectedVMArrayLine|Add-Member-MemberTypeNoteProperty-Name"UsedSpaceGB"-Value"$VMUsedSpaceGB"$UnprotectedVMArray+=$UnprotectedVMArrayLine}#################################################CreatingTargetDatastoreArray################################################$TargetDatastoresURL=$TargetZVMBaseURL+"virtualizationsites/"+$TargetLocalSiteIdentifier+"/datastores"$TargetDatastoresCMD=Invoke-RestMethod-Uri$TargetDatastoresURL-TimeoutSec100-Headers$TargetZVMSessionHeader-ContentType$TypeJSON#Foreachdatastoreforeach($DSin$TargetDatastoresCMD){#GettingvCenterVMIDfromZVRID$DSName=$DS.DatastoreName$DSZVRID=$DS.DatastoreIdentifier$Separator="."$DSZVRIDSplit=$DSZVRID.split($Separator)$DSID=$DSZVRIDSplit[1]$DSID="Datastore-"+$DSID#UsingvCentertogetmoreinfo$DSCluster=Get-Datastore-Id$DSID|Get-DatastoreCluster|select-ExpandPropertyName-First1$DSInfo=Get-Datastore-Id$DSID|select*-First1$DSCapacityGB=$DSInfo.CapacityGB$DSCapacityGB=[math]::Round($DSCapacityGB)$DSFreeSpaceGB=$DSInfo.FreeSpaceGB$DSFreeSpaceGB=[math]::Round($DSFreeSpaceGB)$DSFreePercentage=($DSFreeSpaceGB/$DSCapacityGB)*100$DSFreePercentage=[math]::Round($DSFreePercentage)#Findingifdatastoreisusedforreplication$ResourceReportTargetDatastores=$ResourceReport|select-ExpandPropertyTargetDatastores#CheckingifDSNamefoundinanytargetVMreplicadatastoresif($ResourceReportTargetDatastores-match$DSName){$DSUsedByZVR="TRUE"}else{$DSUsedByZVR="FALSE"}#Buildingarrayline$TargetDatastoreArrayLine=new-objectPSObject$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"PODName"-Value$TargetPOD$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"DatastoreCluster"-Value"$DSCluster"$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"DatastoreName"-Value"$DSName"$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"UsedByZVR"-Value"$DSUsedByZVR"$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"CapacityGB"-Value"$DSCapacityGB"$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"FreeSpaceGB"-Value"$DSFreeSpaceGB"$TargetDatastoreArrayLine|Add-Member-MemberTypeNoteProperty-Name"FreePercent"-Value"$DSFreePercentage"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 46 OF 134

$TargetDatastoreArray+=$TargetDatastoreArrayLine}#################################################CreatingVPGArray,VMArray,VMVolumeArray,VMNICArray#################################################URLtocreateVPGsettings$CreateVPGURL=$SourceZVMBaseURL+"vpgSettings"#BuildListofVPGs$vpgListApiUrl=$SourceZVMBaseURL+"vpgs"$vpgList=Invoke-RestMethod-Uri$vpgListApiUrl-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML#BuildListofVMs$vmListApiUrl=$SourceZVMBaseURL+"vms"$vmList=Invoke-RestMethod-Uri$vmListApiUrl-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML#SelectIDsfromtheAPIarray$zertoprotectiongrouparray=$vpgList.ArrayOfVpgApi.VpgApi|Select-ObjectOrganizationName,vpgname,vmscount,vpgidentifier$vmListarray=$vmList.ArrayOfVmApi.VmApi|select-object*#################################################StartingforeachVPGactionofcollectingZVMVPGdata################################################foreach($VPGLinein$zertoprotectiongrouparray){$VPGidentifier=$VPGLine.vpgidentifier$VPGOrganization=$VPGLine.OrganizationName$VPGVMCount=$VPGLine.VmsCount$JSON="{""VpgIdentifier"":""$VPGidentifier""}"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$SourceZVMSessionHeader_JSON$ValidVPGSettingsIdentifier=$true}Catch{$ValidVPGSettingsIdentifier=$false}#################################################GettingVPGsettingsfromAPI#################################################SkippingifunabletoobtainvalidVPGsettingidentifierif($ValidVPGSettingsIdentifier-eq$true){$VPGSettingsURL=$SourceZVMBaseURL+"vpgSettings/"+$VPGSettingsIdentifier$VPGSettings=Invoke-RestMethod-Uri$VPGSettingsURL-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#GettingrecoverysiteID(neededanywayfornetworksettings)$VPGRecoverySiteIdentifier=$VPGSettings.Basic.RecoverySiteIdentifier#Gettingsiteinfo$VISitesURL=$SourceZVMBaseURL+"virtualizationsites"$VISitesCMD=Invoke-RestMethod-Uri$VISitesURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettingnetworkinfo$VINetworksURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/networks"$VINetworksCMD=Invoke-RestMethod-Uri$VINetworksURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettingdatastoreinfo$VIDatastoresURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/datastores"$VIDatastoresCMD=Invoke-RestMethod-Uri$VIDatastoresURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettingdatastoreclusterinfo$VIDatastoreClustersURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/datastoreclusters"$VIDatastoreClustersCMD=Invoke-RestMethod-Uri$VIDatastoreClustersURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettingfolderinfo$VIFoldersURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/folders"$VIFoldersCMD=Invoke-RestMethod-Uri$VIFoldersURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettingclusterinfo$VIClustersURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/hostclusters"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 47 OF 134

$VIClustersCMD=Invoke-RestMethod-Uri$VIClustersURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettinghostinfo$VIHostsURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/hosts"$VIHostsCMD=Invoke-RestMethod-Uri$VIHostsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#Gettingresourcepoolinfo$VIResourcePoolsURL=$SourceZVMBaseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/resourcepools"$VIResourcePoolsCMD=Invoke-RestMethod-Uri$VIResourcePoolsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#GettingallVPGSettings$VPGJournalHistoryInHours=$VPGSettings.Basic.JournalHistoryInHours$VPGName=$VPGSettings.Basic.Name$VPGPriortiy=$VPGSettings.Basic.Priority$VPGProtectedSiteIdentifier=$VPGSettings.Basic.ProtectedSiteIdentifier$VPGRpoInSeconds=$VPGSettings.Basic.RpoInSeconds$VPGServiceProfileIdentifier=$VPGSettings.Basic.ServiceProfileIdentifier$VPGTestIntervalInMinutes=$VPGSettings.Basic.TestIntervalInMinutes$VPGUseWanCompression=$VPGSettings.Basic.UseWanCompression$VPGZorgIdentifier=$VPGSettings.Basic.ZorgIdentifier#GettingBootGroupIDs$VPGBootGroups=$VPGSettings.BootGroups.BootGroups$VPGBootGroupCount=$VPGSettings.BootGroups.BootGroups.Count$VPGBootGroupNames=$VPGSettings.BootGroups.BootGroups.Name$VPGBootGroupDelays=$VPGSettings.BootGroups.BootGroups.BootDelayInSeconds$VPGBootGroupIdentifiers=$VPGSettings.BootGroups.BootGroups.BootGroupIdentifier#GettingJournalinfo$VPGJournalDatastoreClusterIdentifier=$VPGSettings.Journal.DatastoreClusterIdentifier$VPGJournalDatastoreIdentifier=$VPGSettings.Journal.DatastoreIdentifier$VPGJournalHardLimitInMB=$VPGSettings.Journal.Limitation.HardLimitInMB$VPGJournalHardLimitInPercent=$VPGSettings.Journal.Limitation.HardLimitInPercent$VPGJournalWarningThresholdInMB=$VPGSettings.Journal.Limitation.WarningThresholdInMB$VPGJournalWarningThresholdInPercent=$VPGSettings.Journal.Limitation.WarningThresholdInPercent#GettingNetworkIDs$VPGFailoverNetworkID=$VPGSettings.Networks.Failover.Hypervisor.DefaultNetworkIdentifier$VPGFailoverTestNetworkID=$VPGSettings.Networks.FailoverTest.Hypervisor.DefaultNetworkIdentifier#Gettingrecoveryinfo$VPGDefaultDatastoreIdentifier=$VPGSettings.Recovery.DefaultDatastoreIdentifier$VPGDefaultFolderIdentifier=$VPGSettings.Recovery.DefaultFolderIdentifier$VPGDefaultHostClusterIdentifier=$VPGSettings.Recovery.DefaultHostClusterIdentifier$VPGDefaultHostIdentifier=$VPGSettings.Recovery.DefaultHostIdentifier$VPGResourcePoolIdentifier=$VPGSettings.Recovery.ResourcePoolIdentifier#Gettingscriptinginfo$VPGScriptingPreRecovery=$VPGSettings.Scripting.PreRecovery$VPGScriptingPostRecovery=$VPGSettings.Scripting.PostRecovery#GettingVMIDsinVPG$VPGVMIdentifiers=$VPGSettings.VMs.VmIdentifier#################################################TranslatingZertoIDsfromVPGsettingstofriendlyvSpherenames#################################################Gettingsitenames$VPGProtectedSiteName=$VISitesCMD|Where-Object{$_.SiteIdentifier-eq$VPGProtectedSiteIdentifier}|select-ExpandPropertyVirtualizationSiteName$VPGRecoverySiteName=$VISitesCMD|Where-Object{$_.SiteIdentifier-eq$VPGRecoverySiteIdentifier}|select-ExpandPropertyVirtualizationSiteName#Gettingnetworknames$VPGFailoverNetworkName=$VINetworksCMD|Where-Object{$_.NetworkIdentifier-eq$VPGFailoverNetworkID}|Select-ExpandPropertyVirtualizationNetworkName$VPGFailoverTestNetworkName=$VINetworksCMD|Where-Object{$_.NetworkIdentifier-eq$VPGFailoverTestNetworkID}|Select-ExpandPropertyVirtualizationNetworkName#Gettingdatastoreclustername$VPGJournalDatastoreClusterName=$VIDatastoreClustersCMD|Where-Object{$_.DatastoreClusterIdentifier-eq$VPGJournalDatastoreClusterIdentifier}|select-ExpandPropertyDatastoreClusterName

#Gettingdatastorenames$VPGDefaultDatastoreName=$VIDatastoresCMD|Where-Object{$_.DatastoreIdentifier-eq$VPGDefaultDatastoreIdentifier}|select-ExpandPropertyDatastoreName$VPGJournalDatastoreName=$VIDatastoresCMD|Where-Object{$_.DatastoreIdentifier-eq$VPGJournalDatastoreIdentifier}|select-ExpandPropertyDatastoreName#Gettingfoldername$VPGDefaultFolderName=$VIFoldersCMD|Where-Object{$_.FolderIdentifier-eq$VPGDefaultFolderIdentifier}|select-ExpandPropertyFolderName#Gettingclustername$VPGDefaultHostClusterName=$VIClustersCMD|Where-Object{$_.ClusterIdentifier-eq$VPGDefaultHostClusterIdentifier}|select-ExpandPropertyVirtualizationClusterName#Gettinghostname$VPGDefaultHostName=$VIHostsCMD|Where-Object{$_.HostIdentifier-eq$VPGDefaultHostIdentifier}|select-ExpandPropertyVirtualizationHostName#Gettingresourcepoolname$VPGResourcePoolName=$VIResourcePoolsCMD|Where-Object{$_.ResourcePoolIdentifier-eq$VPGResourcePoolIdentifier}|select-ExpandPropertyResourcepoolName

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 48 OF 134

#################################################AddingallVPGsettinginfoto$VPGArray################################################$VPGArrayLine=new-objectPSObject$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value$SourcePOD$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGidentifier"-Value$VPGidentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGOrganization"-Value$VPGOrganization$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGVMCount"-Value$VPGVMCount$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGPriortiy"-Value$VPGPriortiy$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGProtectedSiteName"-Value$VPGProtectedSiteName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGProtectedSiteIdentifier"-Value$VPGProtectedSiteIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGRecoverySiteName"-Value$VPGRecoverySiteName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGRecoverySiteIdentifier"-Value$VPGRecoverySiteIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGRpoInSeconds"-Value$VPGRpoInSeconds$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGServiceProfileIdentifier"-Value$VPGServiceProfileIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGTestIntervalInMinutes"-Value$VPGTestIntervalInMinutes$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGUseWanCompression"-Value$VPGUseWanCompression$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGZorgIdentifier"-Value$VPGZorgIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGBootGroupCount"-Value$VPGBootGroupCount$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGBootGroupNames"-Value$VPGBootGroupNames$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGBootGroupDelays"-Value$VPGBootGroupDelays$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGBootGroupIdentifiers"-Value$VPGBootGroupIdentifiers$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalHistoryInHours"-Value$VPGJournalHistoryInHours$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalDatastoreClusterName"-Value$VPGJournalDatastoreClusterName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalDatastoreClusterIdentifier"-Value$VPGJournalDatastoreClusterIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalDatastoreName"-Value$VPGJournalDatastoreName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalDatastoreIdentifier"-Value$VPGJournalDatastoreIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalHardLimitInMB"-Value$VPGJournalHardLimitInMB$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalHardLimitInPercent"-Value$VPGJournalHardLimitInPercent$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalWarningThresholdInMB"-Value$VPGJournalWarningThresholdInMB$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGJournalWarningThresholdInPercent"-Value$VPGJournalWarningThresholdInPercent$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGFailoverNetworkName"-Value$VPGFailoverNetworkName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGFailoverNetworkID"-Value$VPGFailoverNetworkID$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGFailoverTestNetworkName"-Value$VPGFailoverTestNetworkName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGFailoverTestNetworkID"-Value$VPGFailoverTestNetworkID$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultDatastoreName"-Value$VPGDefaultDatastoreName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultDatastoreIdentifier"-Value$VPGDefaultDatastoreIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultFolderName"-Value$VPGDefaultFolderName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultFolderIdentifier"-Value$VPGDefaultFolderIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultHostClusterName"-Value$VPGDefaultHostClusterName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultHostClusterIdentifier"-Value$VPGDefaultHostClusterIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultHostName"-Value$VPGDefaultHostName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGDefaultHostIdentifier"-Value$VPGDefaultHostIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGResourcePoolName"-Value$VPGResourcePoolName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGResourcePoolIdentifier"-Value$VPGResourcePoolIdentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGScriptingPreRecovery"-Value$VPGScriptingPreRecovery$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGScriptingPostRecovery"-Value$VPGScriptingPostRecovery$VPGArray+=$VPGArrayLine#################################################StartingforeachVMIDactionforcollectingZVMVMdata################################################foreach($_in$VPGVMIdentifiers){$VMIdentifier=$_#GetVMssettings$GetVMSettingsURL=$SourceZVMBaseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier$GetVMSettings=Invoke-RestMethod-MethodGet-Uri$GetVMSettingsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_JSON-ContentType$TypeJSON#GettingtheVMnameanddiskusage$VMNameArray=$vmListarray|where-object{$_.VmIdentifier-eq$VMIdentifier}|Select-Object*$VMName=$VMNameArray.VmName$VMProvisionedStorageInMB=$VMNameArray.ProvisionedStorageInMB$VMUsedStorageInMB=$VMNameArray.UsedStorageInMB#SettingvariablesfromtheAPI$VMVolumeCount=$GetVMSettings.Volumes.Count$VMNICCount=$GetVMSettings.Nics.Count

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 49 OF 134

$VMBootGroupIdentifier=$GetVMSettings.BootGroupIdentifier$VMJournalDatastoreClusterIdentifier=$GetVMSettings.Journal.DatastoreClusterIdentifier$VMJournalDatastoreIdentifier=$GetVMSettings.Journal.DatastoreIdentifier$VMJournalHardLimitInMB=$GetVMSettings.Journal.Limitation.HardLimitInMB$VMJournalHardLimitInPercent=$GetVMSettings.Journal.Limitation.HardLimitInPercent$VMJournalWarningThresholdInMB=$GetVMSettings.Journal.Limitation.WarningThresholdInMB$VMJournalWarningThresholdInPercent=$GetVMSettings.Journal.Limitation.WarningThresholdInPercent$VMDatastoreClusterIdentifier=$GetVMSettings.Recovery.DatastoreClusterIdentifier$VMDatastoreIdentifier=$GetVMSettings.Recovery.DatastoreIdentifier$VMFolderIdentifier=$GetVMSettings.Recovery.FolderIdentifier$VMHostClusterIdentifier=$GetVMSettings.Recovery.HostClusterIdentifier$VMHostIdentifier=$GetVMSettings.Recovery.HostIdentifier$VMResourcePoolIdentifier=$GetVMSettings.Recovery.ResourcePoolIdentifier#################################################TranslatingZertoIDsfromVMsettingstofriendlyvSpherenames#################################################Gettingbootgroup$VMBootGroupName=$VPGBootGroups|Where-Object{$_.BootGroupIdentifier-eq$VMBootGroupIdentifier}|select-ExpandPropertyName$VMBootGroupDelay=$VPGBootGroups|Where-Object{$_.BootGroupIdentifier-eq$VMBootGroupIdentifier}|select-ExpandPropertyBootDelayInSeconds#Gettingdatastoreclustername$VMJournalDatastoreClusterName=$VIDatastoreClustersCMD|Where-Object{$_.DatastoreClusterIdentifier-eq$VMJournalDatastoreClusterIdentifier}|select-ExpandPropertyDatastoreClusterName

$VMDatastoreClusterName=$VIDatastoreClustersCMD|Where-Object{$_.DatastoreClusterIdentifier-eq$VMDatastoreClusterIdentifier}|select-ExpandPropertyDatastoreClusterName

#Gettingdatastorename$VMJournalDatastoreName=$VIDatastoresCMD|Where-Object{$_.DatastoreIdentifier-eq$VMJournalDatastoreIdentifier}|select-ExpandPropertyDatastoreName$VMDatastoreName=$VIDatastoresCMD|Where-Object{$_.DatastoreIdentifier-eq$VMDatastoreIdentifier}|select-ExpandPropertyDatastoreName#Gettingfoldername$VMFolderName=$VIFoldersCMD|Where-Object{$_.FolderIdentifier-eq$VMFolderIdentifier}|select-ExpandPropertyFolderName#Gettingclustername$VMHostClusterName=$VIClustersCMD|Where-Object{$_.ClusterIdentifier-eq$VMHostClusterIdentifier}|select-ExpandPropertyVirtualizationClusterName#Gettinghostname$VMHostName=$VIHostsCMD|Where-Object{$_.HostIdentifier-eq$VMHostIdentifier}|select-ExpandPropertyVirtualizationHostName#Gettingresourcepoolname$VMResourcePoolName=$VIResourcePoolsCMD|Where-Object{$_.ResourcePoolIdentifier-eq$VMResourcePoolIdentifier}|select-ExpandPropertyResourcepoolName#################################################AddingallVMsettinginfoto$VMArray################################################$VMArrayLine=new-objectPSObject$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value$SourcePOD$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGidentifier"-Value$VPGidentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$VMName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMIdentifier"-Value$VMIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICCount"-Value$VMNICCount$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeCount"-Value$VMVolumeCount$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMProvisionedStorageInMB"-Value$VMProvisionedStorageInMB$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMUsedStorageInMB"-Value$VMUsedStorageInMB$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMBootGroupName"-Value$VMBootGroupName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMBootGroupDelay"-Value$VMBootGroupDelay$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMBootGroupIdentifier"-Value$VMBootGroupIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMJournalDatastoreClusterName"-Value$VMJournalDatastoreClusterName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMJournalDatastoreClusterIdentifier"-Value$VMJournalDatastoreClusterIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMJournalDatastoreName"-Value$VMJournalDatastoreName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMJournalDatastoreIdentifier"-Value$VMJournalDatastoreIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMJournalHardLimitInMB"-Value$VMJournalHardLimitInMB$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMJournalHardLimitInPercent"-Value$VMJournalHardLimitInPercent$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMDatastoreClusterName"-Value$VMDatastoreClusterName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMDatastoreClusterIdentifier"-Value$VMDatastoreClusterIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMDatastoreName"-Value$VMDatastoreName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMDatastoreIdentifier"-Value$VMDatastoreIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMFolderName"-Value$VMFolderName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMFolderIdentifier"-Value$VMFolderIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMHostClusterName"-Value$VMHostClusterName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMHostClusterIdentifier"-Value$VMHostClusterIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMHostName"-Value$VMHostName$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMHostIdentifier"-Value$VMHostIdentifier$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMResourcePoolName"-Value$VMResourcePoolName

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 50 OF 134

$VMArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMResourcePoolIdentifier"-Value$VMResourcePoolIdentifier$VMArray+=$VMArrayLine#################################################GetVMVolumesettingsforthecurrentVPG################################################$GetVMSettingVolumesURL=$SourceZVMBaseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/volumes"$GetVMSettingVolumes=Invoke-RestMethod-MethodGet-Uri$GetVMSettingVolumesURL-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML$GetVMSettingVolumeIDs=$GetVMSettingVolumes.ArrayOfVpgSettingsVmVolumeApi.VpgSettingsVmVolumeApi|select-objectVolumeIdentifier-ExpandPropertyVolumeIdentifier#################################################StartingforeachVMVolumeIDactionforcollectingZVMVMVolumedata################################################foreach($_in$GetVMSettingVolumeIDs){$VMVolumeID=$_#GettingAPIdataforvolume$GetVMSettingVolumeURL=$SourceZVMBaseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/volumes/"+$VMVolumeID$GetVMSettingVolume=Invoke-RestMethod-MethodGet-Uri$GetVMSettingVolumeURL-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML#Settingvalues$VMVolumeDatastoreClusterIdentifier=$GetVMSettingVolume.VpgSettingsVmVolumeApi.Datastore.DatastoreClusterIdentifier$VMVolumeDatastoreIdentifier=$GetVMSettingVolume.VpgSettingsVmVolumeApi.Datastore.DatastoreIdentifier$VMVolumeIsSWAP=$GetVMSettingVolume.VpgSettingsVmVolumeApi.IsSwap$VMVolumeIsThin=$GetVMSettingVolume.VpgSettingsVmVolumeApi.Datastore.IsThin#Gettingdatastoreclustername$VMVolumeDatastoreClusterName=$VIDatastoreClustersCMD|Where-Object{$_.DatastoreClusterIdentifier-eq$VMVolumeDatastoreClusterIdentifier}|select-ExpandPropertyDatastoreClusterName#Gettingdatastorename$VMVolumeDatastoreName=$VIDatastoresCMD|Where-Object{$_.DatastoreIdentifier-eq$VMVolumeDatastoreIdentifier}|select-ExpandPropertyDatastoreName#################################################AddingallVMVolumesettinginfoto$VMVolumeArray################################################$VMVolumeArrayLine=new-objectPSObject$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value$SourcePOD$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGidentifier"-Value$VPGidentifier$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$VMName$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMIdentifier"-Value$VMIdentifier$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeID"-Value$VMVolumeID$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeIsSWAP"-Value$VMVolumeIsSWAP$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeIsThin"-Value$VMVolumeIsThin$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeDatastoreClusterName"-Value$VMVolumeDatastoreClusterName$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeDatastoreClusterIdentifier"-Value$VMVolumeDatastoreClusterIdentifier$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeDatastoreName"-Value$VMVolumeDatastoreName$VMVolumeArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMVolumeDatastoreIdentifier"-Value$VMVolumeDatastoreIdentifier$VMVolumeArray+=$VMVolumeArrayLine}#################################################GetVMNicsettingsforthecurrentVPG################################################$GetVMSettingNICsURL=$SourceZVMBaseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics"$GetVMSettingNICs=Invoke-RestMethod-MethodGet-Uri$GetVMSettingNICsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML$VMNICIDs=$GetVMSettingNICs.ArrayOfVpgSettingsVmNicApi.VpgSettingsVmNicApi|select-objectNicIdentifier-ExpandPropertyNicIdentifier#################################################StartingforeachVMNICIDactionforcollectingZVMVMNICdata################################################foreach($_in$VMNICIDs){$VMNICIdentifier=$_$GetVMSettingNICURL=$SourceZVMBaseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics/"+$VMNICIdentifier$GetVMSettingNIC=Invoke-RestMethod-MethodGet-Uri$GetVMSettingNICURL-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML#Buildingarrays$VMSettingNICIDArray1=$GetVMSettingNIC.VpgSettingsVmNicApi.Failover.Hypervisor$VMSettingNICIDArray2=$GetVMSettingNIC.VpgSettingsVmNicApi.Failover.Hypervisor.IpConfig$VMSettingNICIDArray3=$GetVMSettingNIC.VpgSettingsVmNicApi.FailoverTest.Hypervisor$VMSettingNICIDArray4=$GetVMSettingNIC.VpgSettingsVmNicApi.FailoverTest.Hypervisor.IpConfig#Settingfailovervalues$VMNICFailoverDNSSuffix=$VMSettingNICIDArray1.DnsSuffix$VMNICFailoverNetworkIdentifier=$VMSettingNICIDArray1.NetworkIdentifier

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 51 OF 134

$VMNICFailoverShouldReplaceMacAddress=$VMSettingNICIDArray1.ShouldReplaceMacAddress$VMNICFailoverGateway=$VMSettingNICIDArray2.Gateway$VMNIsFailoverDHCP=$VMSettingNICIDArray2.IsDhcp$VMNICFailoverPrimaryDns=$VMSettingNICIDArray2.PrimaryDns$VMNICFailoverSecondaryDns=$VMSettingNICIDArray2.SecondaryDns$VMNICFailoverStaticIp=$VMSettingNICIDArray2.StaticIp$VMNICFailoverSubnetMask=$VMSettingNICIDArray2.SubnetMask#Nullingblankcontentif($VMNICFailoverDNSSuffix.nil-eq$true){$VMNICFailoverDNSSuffix=$null}if($VMNICFailoverGateway.nil-eq$true){$VMNICFailoverGateway=$null}if($VMNICFailoverPrimaryDns.nil-eq$true){$VMNICFailoverPrimaryDns=$null}if($VMNICFailoverSecondaryDns.nil-eq$true){$VMNICFailoverSecondaryDns=$null}if($VMNICFailoverStaticIp.nil-eq$true){$VMNICFailoverStaticIp=$null}if($VMNICFailoverSubnetMask.nil-eq$true){$VMNICFailoverSubnetMask=$null}#Settingfailovertestvalues$VMNICFailoverTestDNSSuffix=$VMSettingNICIDArray3.DnsSuffix$VMNICFailoverTestNetworkIdentifier=$VMSettingNICIDArray3.NetworkIdentifier$VMNICFailoverTestShouldReplaceMacAddress=$VMSettingNICIDArray3.ShouldReplaceMacAddress$VMNICFailoverTestGateway=$VMSettingNICIDArray4.Gateway$VMNIsFailoverTestDHCP=$VMSettingNICIDArray4.IsDhcp$VMNICFailoverTestPrimaryDns=$VMSettingNICIDArray4.PrimaryDns$VMNICFailoverTestSecondaryDns=$VMSettingNICIDArray4.SecondaryDns$VMNICFailoverTestStaticIp=$VMSettingNICIDArray4.StaticIp$VMNICFailoverTestSubnetMask=$VMSettingNICIDArray4.SubnetMask#Nullingblankcontentif($VMNICFailoverTestDNSSuffix.nil-eq$true){$VMNICFailoverTestDNSSuffix=$null}if($VMNICFailoverTestGateway.nil-eq$true){$VMNICFailoverTestGateway=$null}if($VMNICFailoverTestPrimaryDns.nil-eq$true){$VMNICFailoverTestPrimaryDns=$null}if($VMNICFailoverTestSecondaryDns.nil-eq$true){$VMNICFailoverTestSecondaryDns=$null}if($VMNICFailoverTestStaticIp.nil-eq$true){$VMNICFailoverTestStaticIp=$null}if($VMNICFailoverTestSubnetMask.nil-eq$true){$VMNICFailoverTestSubnetMask=$null}#MappingNetworkIDstoNames$VMNICFailoverNetworkName=$VINetworksCMD|Where-Object{$_.NetworkIdentifier-eq$VMNICFailoverNetworkIdentifier}|SelectVirtualizationNetworkName-ExpandPropertyVirtualizationNetworkName

$VMNICFailoverTestNetworkName=$VINetworksCMD|Where-Object{$_.NetworkIdentifier-eq$VMNICFailoverTestNetworkIdentifier}|SelectVirtualizationNetworkName-ExpandPropertyVirtualizationNetworkName

#################################################AddingallVMNICsettinginfoto$VMNICArray################################################$VMNICArrayLine=new-objectPSObject$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourcePOD"-Value$SourcePOD$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGidentifier"-Value$VPGidentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$VMName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMIdentifier"-Value$VMIdentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICIdentifier"-Value$VMNICIdentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverNetworkName"-Value$VMNICFailoverNetworkName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverNetworkIdentifier"-Value$VMNICFailoverNetworkIdentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverDNSSuffix"-Value$VMNICFailoverDNSSuffix$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverShouldReplaceMacAddress"-Value$VMNICFailoverShouldReplaceMacAddress$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverGateway"-Value$VMNICFailoverGateway$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverDHCP"-Value$VMNIsFailoverDHCP$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverPrimaryDns"-Value$VMNICFailoverPrimaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverSecondaryDns"-Value$VMNICFailoverSecondaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverStaticIp"-Value$VMNICFailoverStaticIp$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverSubnetMask"-Value$VMNICFailoverSubnetMask$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestNetworkName"-Value$VMNICFailoverTestNetworkName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestNetworkIdentifier"-Value$VMNICFailoverTestNetworkIdentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestDNSSuffix"-Value$VMNICFailoverTestDNSSuffix$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestShouldReplaceMacAddress"-Value$VMNICFailoverTestShouldReplaceMacAddress$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestGateway"-Value$VMNICFailoverTestGateway$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestDHCP"-Value$VMNIsFailoverTestDHCP$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestPrimaryDns"-Value$VMNICFailoverTestPrimaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestSecondaryDns"-Value$VMNICFailoverTestSecondaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestStaticIp"-Value$VMNICFailoverTestStaticIp$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestSubnetMask"-Value$VMNICFailoverTestSubnetMask$VMNICArray+=$VMNICArrayLine#EndofperVMNICactionsbelow

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 52 OF 134

}#EndofperVMNICactionsabove##EndofperVMactionsbelow}#EndofperVMactionsabove#################################################DeletingVPGeditsettingsID(sameasclosingtheeditscreenonaVPGintheZVMwithoutmakinganychanges)################################################Try{Invoke-RestMethod-MethodDelete-Uri$VPGSettingsURL-TimeoutSec100-Headers$SourceZVMSessionHeader_XML-ContentType$TypeXML}Catch[system.exception]{}##EndofcheckforvalidVPGsettingsIDbelow}#EndofcheckforvalidVPGsettingsIDabove##EndofperVPGactionsbelow}#EndofperVPGactionsabove##################################################BuildingReportNo.x-Zertodesignedreport,summaryofPODs#################################################BuildingSourcePODSummarydata$PODProtectedArray=$ProtectedVPGArray|Where-Object{$_.SourcePOD-eq$SourcePOD}$PODTotalVPGs=$PODProtectedArray|Measure-Object|select-ExpandPropertyCount$PODTotalVPGsMeetingSLA=$PODProtectedArray|Where-Object{$_.Status-eq"MeetingSLA"}|Measure-Object|select-ExpandPropertyCount$PODTotalVPGsNotMeetingSLA=$PODProtectedArray|Where-Object{$_.Status-ne"MeetingSLA"}|Measure-Object|select-ExpandPropertyCount$PODTotalHighPriorityVPGs=$PODProtectedArray|Where-Object{$_.Priority-eq"High"}|Measure-Object|select-ExpandPropertyCount$PODTotalMediumPriorityVPGs=$PODProtectedArray|Where-Object{$_.Priority-eq"Medium"}|Measure-Object|select-ExpandPropertyCount$PODTotalLowPriorityVPGs=$PODProtectedArray|Where-Object{$_.Priority-eq"Low"}|Measure-Object|select-ExpandPropertyCount$PODAverageRPO=$PODProtectedArray|selectRPO|Measure-Object|select-ExpandPropertyCount$PODTotalProtectedSizeGB=($PODProtectedArray.SizeInGb|Measure-Object-Sum).Sum$PODTotalProtectedSizeTB=$PODTotalProtectedSizeGB/1024$PODTotalProtectedSizeTB=[math]::Round($PODTotalProtectedSizeTB,2)$PODTotalJournalSizeGB=($PODProtectedArray.JournalSizeInGb|Measure-Object-Sum).Sum$PODTotalJournalSizeTB=$PODTotalJournalSizeGB/1024$PODTotalJournalSizeTB=[math]::Round($PODTotalJournalSizeTB,2)#GettingProtectedVMtotals$PODProtectedVMArray=$ProtectedVMArray|Where-Object{$_.SourcePOD-eq$SourcePOD}$PODProtectedVMs=$PODProtectedVMArray|Measure-Object|select-ExpandPropertyCount#GettingUnProtectedVMtotals$PODUnProtectedVMArray=$UnprotectedVMArray|Where-Object{$_.SourcePOD-eq$SourcePOD}$PODUnProtectedVMs=$PODUnProtectedVMArray|Measure-Object|select-ExpandPropertyCount#PODtotalVMs$PODTotalVMs=$PODProtectedVMs+$PODUnProtectedVMs#Addingarrayline$PODSummaryArrayLine=new-objectPSObject$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"PODName"-Value"$SourcePOD"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMs"-Value"$PODTotalVMs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMsUnProtected"-Value"$PODUnProtectedVMs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMsProtected"-Value"$PODProtectedVMs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGs"-Value"$PODTotalVPGs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"MeetingSLA"-Value"$PODTotalVPGsMeetingSLA"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"NotMeetingSLA"-Value"$PODTotalVPGsNotMeetingSLA"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"AverageRPO"-Value"$PODAverageRPO"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"HighPriority"-Value"$PODTotalHighPriorityVPGs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"MediumPriority"-Value"$PODTotalMediumPriorityVPGs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"LowPriority"-Value"$PODTotalLowPriorityVPGs"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"ProtectedSizeTB"-Value"$PODTotalProtectedSizeTB"$PODSummaryArrayLine|Add-Member-MemberTypeNoteProperty-Name"JournalSizeTB"-Value"$PODTotalJournalSizeTB"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 53 OF 134

$PODSummaryArray+=$PODSummaryArrayLine#DisconnectingfromtargetvCenter,nolongerneededDisconnect-VIServer*-confirm:$false#EndoffailedvCenterauthbelow}#EndoffailedvCenterauthaboveelse{#FailedvCenterauth,notrunningreportsforPODwrite-host"FailedtologintovCenter:$SourcevCenterSkippingreportsforPOD:$SourcePOD"}#EndofforeachPODbelow}#EndofforeachPODabove##################################################FunctionforbuildingHTMLtableforProtectedVPGArray################################################FunctionCreate-ProtectedVPGArrayTable{Param($Array,$TableCaption)$ProtectedVPGArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">SourcePOD</th><thclass="tg-foxd">TargetPOD</th><thclass="tg-foxd">VPGName</th><thclass="tg-foxd">VMCount</th><thclass="tg-foxd">Priority</th><thclass="tg-foxd">RPO</th><thclass="tg-foxd">RPOAlerts</th><thclass="tg-foxd">Status</th><thclass="tg-foxd">SizeInGB</th><thclass="tg-foxd">JournalSizeInGB</th><thclass="tg-foxd">AlertDescription</th></tr>"@#BuildingHTMLtable$ProtectedVPGArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$SourcePOD=$_.SourcePOD$TargetPOD=$_.TargetPOD$VPGName=$_.VPGName$VMCount=$_.VMCount$Priority=$_.Priority$RPO=$_.RPO$RPOAlerts=$_.RPOAlerts$Status=$_.Status$SizeInGb=$_.SizeInGb$JournalSizeInGb=$_.JournalSizeInGb$AlertDescription=$_.AlertDescription#BuildingHTMLtablerow$ProtectedVPGArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$SourcePOD</td><tdclass=""tg-yw4l"">$TargetPOD</td><tdclass=""tg-yw4l"">$VPGName</td><tdclass=""tg-yw4l"">$VMCount</td><tdclass=""tg-yw4l"">$Priority</td><tdclass=""tg-yw4l"">$RPO</td><tdclass=""tg-yw4l"">$RPOAlerts</td><tdclass=""tg-yw4l"">$Status</td><tdclass=""tg-yw4l"">$SizeInGb</td>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 54 OF 134

<tdclass=""tg-yw4l"">$JournalSizeInGb</td><tdclass=""tg-yw4l"">$AlertDescription</td></tr>"#Addingrowstotable$ProtectedVPGArrayHTMLTable+=$ProtectedVPGArrayHTMLTableRow}#CompilingEndofHTMLemail$ProtectedVPGArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$ProtectedVPGArrayHTMLTable=$ProtectedVPGArrayHTMLTableStart+$ProtectedVPGArrayHTMLTable+$ProtectedVPGArrayHTMLTableEnd$ProtectedVPGArrayHTMLTable}#################################################FunctionforbuildingHTMLtableforProtectedVMArray################################################FunctionCreate-ProtectedVMArrayTable{Param($Array,$TableCaption)$ProtectedVMArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">SourcePOD</th><thclass="tg-foxd">SourceCluster</th><thclass="tg-foxd">TargetPOD</th><thclass="tg-foxd">TargetCluster</th><thclass="tg-foxd">VPGName</th><thclass="tg-foxd">VMName</th><thclass="tg-foxd">Priority</th><thclass="tg-foxd">Status</th><thclass="tg-foxd">RPO</th><thclass="tg-foxd">Disks</th><thclass="tg-foxd">SizeInGB</th><thclass="tg-foxd">JournalSizeInGB</th></tr>"@#BuildingHTMLtable$ProtectedVMArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$SourcePOD=$_.SourcePOD$SourceCluster=$_.SourceCluster$TargetPOD=$_.TargetPOD$TargetCluster=$_.TargetCluster$VPGName=$_.VPGName$VMName=$_.VMName$Priority=$_.Priority$Status=$_.Status$RPO=$_.RPO$Disks=$_.Disks$SizeInGb=$_.SizeInGb$JournalSizeInGb=$_.JournalSizeInGb#BuildingHTMLtablerow$ProtectedVMArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$SourcePOD</td><tdclass=""tg-yw4l"">$SourceCluster</td><tdclass=""tg-yw4l"">$TargetPOD</td><tdclass=""tg-yw4l"">$TargetCluster</td><tdclass=""tg-yw4l"">$VPGName</td><tdclass=""tg-yw4l"">$VMName</td><tdclass=""tg-yw4l"">$Priority</td>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 55 OF 134

<tdclass=""tg-yw4l"">$Status</td><tdclass=""tg-yw4l"">$RPO</td><tdclass=""tg-yw4l"">$Disks</td><tdclass=""tg-yw4l"">$SizeInGb</td><tdclass=""tg-yw4l"">$JournalSizeInGb</td></tr>"#Addingrowstotable$ProtectedVMArrayHTMLTable+=$ProtectedVMArrayHTMLTableRow}#CompilingEndofHTMLemail$ProtectedVMArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$ProtectedVMArrayHTMLTable=$ProtectedVMArrayHTMLTableStart+$ProtectedVMArrayHTMLTable+$ProtectedVMArrayHTMLTableEnd$ProtectedVMArrayHTMLTable#EndofProtectedVMArrayTablefunction}#################################################FunctionforbuildingHTMLtableforTargetVRAArray################################################FunctionCreate-TargetVRAArrayTable{Param($Array,$TableCaption)$TargetVRAArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">TargetPOD</th><thclass="tg-foxd">VRACluster</th><thclass="tg-foxd">RecoveryVRAName</th><thclass="tg-foxd">ESXiHostname</th><thclass="tg-foxd">VRAVPGs</th><thclass="tg-foxd">VRAVMs</th><thclass="tg-foxd">VRAVolumes</th><thclass="tg-foxd">VRAVolumesTB</th><thclass="tg-foxd">VRAJournalsTB</th><thclass="tg-foxd">VMNumbervCPU</th><thclass="tg-foxd">VMCpuUsedGhz</th><thclass="tg-foxd">VMMemoryGB</th><thclass="tg-foxd">VMActiveMemoryGB</th></tr>"@#BuildingHTMLtable$TargetVRAArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$TargetPOD=$_.TargetPOD$TargetCluster=$_.VRACluster$VRAName=$_.VRAName$ESXiHostname=$_.ESXiHostname$VRAVPGs=$_.VRAVPGs$VRAVMs=$_.VRAVMs$VRAVolumes=$_.VRAVolumes$VRARecoveryVolumesInTB=$_.VRARecoveryVolumesInTB$VRARecoveryJournalsInTB=$_.VRARecoveryJournalsInTB$VMNumberOfvCPU=$_.VMNumberOfvCPU$VMCpuUsedInGhz=$_.VMCpuUsedInGhz$VMMemoryInGB=$_.VMMemoryInGB$VMActiveMemoryInGB=$_.VMActiveMemoryInGB#BuildingHTMLtablerow$TargetVRAArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$TargetPOD</td>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 56 OF 134

<tdclass=""tg-yw4l"">$TargetCluster</td><tdclass=""tg-yw4l"">$VRAName</td><tdclass=""tg-yw4l"">$ESXiHostname</td><tdclass=""tg-yw4l"">$VRAVPGs</td><tdclass=""tg-yw4l"">$VRAVMs</td><tdclass=""tg-yw4l"">$VRAVolumes</td><tdclass=""tg-yw4l"">$VRARecoveryVolumesInTB</td><tdclass=""tg-yw4l"">$VRARecoveryJournalsInTB</td><tdclass=""tg-yw4l"">$VMNumberOfvCPU</td><tdclass=""tg-yw4l"">$VMCpuUsedInGhz</td><tdclass=""tg-yw4l"">$VMMemoryInGB</td><tdclass=""tg-yw4l"">$VMActiveMemoryInGB</td></tr>"#Addingrowstotable$TargetVRAArrayHTMLTable+=$TargetVRAArrayHTMLTableRow}#CompilingEndofHTMLemail$TargetVRAArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$TargetVRAArrayHTMLTable=$TargetVRAArrayHTMLTableStart+$TargetVRAArrayHTMLTable+$TargetVRAArrayHTMLTableEnd$TargetVRAArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforUnprotectedVMArrayTable################################################FunctionCreate-UnprotectedVMArrayTable{Param($Array,$TableCaption)$UnprotectedVMArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">SourcePOD</th><thclass="tg-foxd">VMFolder</th><thclass="tg-foxd">VMName</th><thclass="tg-foxd">VMCluster</th><thclass="tg-foxd">NumCPU</th><thclass="tg-foxd">MemoryGB</th><thclass="tg-foxd">NICS</th><thclass="tg-foxd">HardDisks</th><thclass="tg-foxd">UsedSpaceGB</th></tr>"@#BuildingHTMLtable$UnprotectedVMArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$SourcePOD=$_.SourcePOD$VMFolder=$_.VMFolder$VMName=$_.VMName$VMCluster=$_.VMCluster$NumCPU=$_.NumCPU$MemoryGB=$_.MemoryGB$NICS=$_.NICS$HardDisks=$_.HardDisks$UsedSpaceGB=$_.UsedSpaceGB#BuildingHTMLtablerow$UnprotectedVMArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$SourcePOD</td><tdclass=""tg-yw4l"">$VMFolder</td>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 57 OF 134

<tdclass=""tg-yw4l"">$VMName</td><tdclass=""tg-yw4l"">$VMCluster</td><tdclass=""tg-yw4l"">$NumCPU</td><tdclass=""tg-yw4l"">$MemoryGB</td><tdclass=""tg-yw4l"">$NICS</td><tdclass=""tg-yw4l"">$HardDisks</td><tdclass=""tg-yw4l"">$UsedSpaceGB</td></tr>"#Addingrowstotable$UnprotectedVMArrayHTMLTable+=$UnprotectedVMArrayHTMLTableRow}#CompilingEndofHTMLemail$UnprotectedVMArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$UnprotectedVMArrayHTMLTable=$UnprotectedVMArrayHTMLTableStart+$UnprotectedVMArrayHTMLTable+$UnprotectedVMArrayHTMLTableEnd$UnprotectedVMArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforTargetDatastoreArray################################################FunctionCreate-TargetDatastoreArrayTable{Param($Array,$TableCaption)$TargetDatastoreArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">PODName</th><thclass="tg-foxd">DatastoreCluster</th><thclass="tg-foxd">DatastoreName</th><thclass="tg-foxd">UsedByZVR</th><thclass="tg-foxd">CapacityGB</th><thclass="tg-foxd">FreeSpaceGB</th><thclass="tg-foxd">FreePercent</th></tr>"@#BuildingHTMLtable$TargetDatastoreArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$PODName=$_.PODName$DatastoreCluster=$_.DatastoreCluster$DatastoreName=$_.DatastoreName$UsedByZVR=$_.UsedByZVR$CapacityGB=$_.CapacityGB$FreeSpaceGB=$_.FreeSpaceGB$FreePercent=$_.FreePercent#BuildingHTMLtablerow$TargetDatastoreArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$PODName</td><tdclass=""tg-yw4l"">$DatastoreCluster</td><tdclass=""tg-yw4l"">$DatastoreName</td><tdclass=""tg-yw4l"">$UsedByZVR</td><tdclass=""tg-yw4l"">$CapacityGB</td><tdclass=""tg-yw4l"">$FreeSpaceGB</td><tdclass=""tg-yw4l"">$FreePercent</td></tr>"#Addingrowstotable$TargetDatastoreArrayHTMLTable+=$TargetDatastoreArrayHTMLTableRow

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 58 OF 134

}#CompilingEndofHTMLemail$TargetDatastoreArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$TargetDatastoreArrayHTMLTable=$TargetDatastoreArrayHTMLTableStart+$TargetDatastoreArrayHTMLTable+$TargetDatastoreArrayHTMLTableEnd$TargetDatastoreArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforVPGArray################################################FunctionCreate-VPGArrayTable{Param($Array,$TableCaption)$VPGArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">PODName</th><thclass="tg-foxd">VPGName</th><thclass="tg-foxd">VMCount</th><thclass="tg-foxd">Priortiy</th><thclass="tg-foxd">ProtectedSiteName</th><thclass="tg-foxd">RecoverySiteName</th><thclass="tg-foxd">RpoInSeconds</th><thclass="tg-foxd">TestIntervalInMinutes</th><thclass="tg-foxd">UseWanCompression</th><thclass="tg-foxd">BootGroupCount</th><thclass="tg-foxd">BootGroupNames</th><thclass="tg-foxd">BootGroupDelays</th><thclass="tg-foxd">JournalHistoryInHours</th><thclass="tg-foxd">JournalDatastoreClusterName</th><thclass="tg-foxd">JournalDatastoreName</th><thclass="tg-foxd">JournalHardLimitInMB</th><thclass="tg-foxd">JournalHardLimitInPercent</th><thclass="tg-foxd">JournalWarningThresholdInMB</th><thclass="tg-foxd">JournalWarningThresholdInPercent</th><thclass="tg-foxd">FailoverNetworkName</th><thclass="tg-foxd">FailoverTestNetworkName</th><thclass="tg-foxd">DefaultDatastoreName</th><thclass="tg-foxd">DefaultFolderName</th><thclass="tg-foxd">DefaultHostClusterName</th><thclass="tg-foxd">DefaultHostName</th></tr>"@#BuildingHTMLtable$VPGArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$PODName=$_.SourcePOD$VPGName=$_.VPGName$VPGidentifier=$_.VPGidentifier$VPGOrganization=$_.VPGOrganization$VPGVMCount=$_.VPGVMCount$VPGPriortiy=$_.VPGPriortiy$VPGProtectedSiteName=$_.VPGProtectedSiteName$VPGProtectedSiteIdentifier=$_.VPGProtectedSiteIdentifier$VPGRecoverySiteName=$_.VPGRecoverySiteName$VPGRecoverySiteIdentifier=$_.VPGRecoverySiteIdentifier$VPGRpoInSeconds=$_.VPGRpoInSeconds$VPGServiceProfileIdentifier=$_.VPGServiceProfileIdentifier$VPGTestIntervalInMinutes=$_.VPGTestIntervalInMinutes$VPGUseWanCompression=$_.VPGUseWanCompression

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 59 OF 134

$VPGZorgIdentifier=$_.VPGZorgIdentifier$VPGBootGroupCount=$_.VPGBootGroupCount$VPGBootGroupNames=$_.VPGBootGroupNames$VPGBootGroupDelays=$_.VPGBootGroupDelays$VPGBootGroupIdentifiers=$_.VPGBootGroupIdentifiers$VPGJournalHistoryInHours=$_.VPGJournalHistoryInHours$VPGJournalDatastoreClusterName=$_.VPGJournalDatastoreClusterName$VPGJournalDatastoreClusterIdentifier=$_.VPGJournalDatastoreClusterIdentifier$VPGJournalDatastoreName=$_.VPGJournalDatastoreName$VPGJournalDatastoreIdentifier=$_.VPGJournalDatastoreIdentifier$VPGJournalHardLimitInMB=$_.VPGJournalHardLimitInMB$VPGJournalHardLimitInPercent=$_.VPGJournalHardLimitInPercent$VPGJournalWarningThresholdInMB=$_.VPGJournalWarningThresholdInMB$VPGJournalWarningThresholdInPercent=$_.VPGJournalWarningThresholdInPercent$VPGFailoverNetworkName=$_.VPGFailoverNetworkName$VPGFailoverNetworkID=$_.VPGFailoverNetworkID$VPGFailoverTestNetworkName=$_.VPGFailoverTestNetworkName$VPGFailoverTestNetworkID=$_.VPGFailoverTestNetworkID$VPGDefaultDatastoreName=$_.VPGDefaultDatastoreName$VPGDefaultDatastoreIdentifier=$_.VPGDefaultDatastoreIdentifier$VPGDefaultFolderName=$_.VPGDefaultFolderName$VPGDefaultFolderIdentifier=$_.VPGDefaultFolderIdentifier$VPGDefaultHostClusterName=$_.VPGDefaultHostClusterName$VPGDefaultHostClusterIdentifier=$_.VPGDefaultHostClusterIdentifier$VPGDefaultHostName=$_.VPGDefaultHostName$VPGDefaultHostIdentifier=$_.VPGDefaultHostIdentifier$VPGResourcePoolName=$_.VPGResourcePoolName$VPGResourcePoolIdentifier=$_.VPGResourcePoolIdentifier$VPGScriptingPreRecovery=$_.VPGScriptingPreRecovery$VPGScriptingPostRecovery=$_.VPGScriptingPostRecovery#BuildingHTMLtablerow$VPGArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$PODName</td><tdclass=""tg-yw4l"">$VPGName</td><tdclass=""tg-yw4l"">$VPGVMCount</td><tdclass=""tg-yw4l"">$VPGPriortiy</td><tdclass=""tg-yw4l"">$VPGProtectedSiteName</td><tdclass=""tg-yw4l"">$VPGRecoverySiteName</td><tdclass=""tg-yw4l"">$VPGRpoInSeconds</td><tdclass=""tg-yw4l"">$VPGTestIntervalInMinutes</td><tdclass=""tg-yw4l"">$VPGUseWanCompression</td><tdclass=""tg-yw4l"">$VPGBootGroupCount</td><tdclass=""tg-yw4l"">$VPGBootGroupNames</td><tdclass=""tg-yw4l"">$VPGBootGroupDelays</td><tdclass=""tg-yw4l"">$VPGJournalHistoryInHours</td><tdclass=""tg-yw4l"">$VPGJournalDatastoreClusterName</td><tdclass=""tg-yw4l"">$VPGJournalDatastoreName</td><tdclass=""tg-yw4l"">$VPGJournalHardLimitInMB</td><tdclass=""tg-yw4l"">$VPGJournalHardLimitInPercent</td><tdclass=""tg-yw4l"">$VPGJournalWarningThresholdInMB</td><tdclass=""tg-yw4l"">$VPGJournalWarningThresholdInPercent</td><tdclass=""tg-yw4l"">$VPGFailoverNetworkName</td><tdclass=""tg-yw4l"">$VPGFailoverTestNetworkName</td><tdclass=""tg-yw4l"">$VPGDefaultDatastoreName</td><tdclass=""tg-yw4l"">$VPGDefaultFolderName</td><tdclass=""tg-yw4l"">$VPGDefaultHostClusterName</td><tdclass=""tg-yw4l"">$VPGDefaultHostName</td></tr>"#Addingrowstotable$VPGArrayHTMLTable+=$VPGArrayHTMLTableRow}#CompilingEndofHTMLemail$VPGArrayHTMLTableEnd=@"</table>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 60 OF 134

<br>"@#CompilingFinalHTML$VPGArrayHTMLTable=$VPGArrayHTMLTableStart+$VPGArrayHTMLTable+$VPGArrayHTMLTableEnd$VPGArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforVMArray################################################FunctionCreate-VMArrayTable{Param($Array,$TableCaption)$VMArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">PODName</th><thclass="tg-foxd">VPGName</th><thclass="tg-foxd">VMName</th><thclass="tg-foxd">NICCount</th><thclass="tg-foxd">VolumeCount</th><thclass="tg-foxd">ProvisionedStorageInMB</th><thclass="tg-foxd">UsedStorageInMB</th><thclass="tg-foxd">BootGroupName</th><thclass="tg-foxd">BootGroupDelay</th><thclass="tg-foxd">JournalDatastoreClusterName</th><thclass="tg-foxd">JournalDatastoreName</th><thclass="tg-foxd">JournalHardLimitInMB</th><thclass="tg-foxd">JournalHardLimitInPercent</th><thclass="tg-foxd">DatastoreClusterName</th><thclass="tg-foxd">DatastoreName</th><thclass="tg-foxd">FolderName</th><thclass="tg-foxd">HostClusterName</th><thclass="tg-foxd">HostName</th></tr>"@#BuildingHTMLtable$VMArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$PODName=$_.SourcePOD$VPGName=$_.VPGName$VPGidentifier=$_.VPGidentifier$VMName=$_.VMName$VMIdentifier=$_.VMIdentifier$VMNICCount=$_.VMNICCount$VMVolumeCount=$_.VMVolumeCount$VMProvisionedStorageInMB=$_.VMProvisionedStorageInMB$VMUsedStorageInMB=$_.VMUsedStorageInMB$VMBootGroupName=$_.VMBootGroupName$VMBootGroupDelay=$_.VMBootGroupDelay$VMBootGroupIdentifier=$_.VMBootGroupIdentifier$VMJournalDatastoreClusterName=$_.VMJournalDatastoreClusterName$VMJournalDatastoreClusterIdentifier=$_.VMJournalDatastoreClusterIdentifier$VMJournalDatastoreName=$_.VMJournalDatastoreName$VMJournalDatastoreIdentifier=$_.VMJournalDatastoreIdentifier$VMJournalHardLimitInMB=$_.VMJournalHardLimitInMB$VMJournalHardLimitInPercent=$_.VMJournalHardLimitInPercent$VMDatastoreClusterName=$_.VMDatastoreClusterName$VMDatastoreClusterIdentifier=$_.VMDatastoreClusterIdentifier$VMDatastoreName=$_.VMDatastoreName$VMDatastoreIdentifier=$_.VMDatastoreIdentifier$VMFolderName=$_.VMFolderName$VMFolderIdentifier=$_.VMFolderIdentifier$VMHostClusterName=$_.VMHostClusterName

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 61 OF 134

$VMHostClusterIdentifier=$_.VMHostClusterIdentifier$VMHostName=$_.VMHostName$VMHostIdentifier=$_.VMHostIdentifier$VMResourcePoolName=$_.VMResourcePoolName$VMResourcePoolIdentifier=$_.VMResourcePoolIdentifier#BuildingHTMLtablerow$VMArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$PODName</td><tdclass=""tg-yw4l"">$VPGName</td><tdclass=""tg-yw4l"">$VMName</td><tdclass=""tg-yw4l"">$VMNICCount</td><tdclass=""tg-yw4l"">$VMVolumeCount</td><tdclass=""tg-yw4l"">$VMProvisionedStorageInMB</td><tdclass=""tg-yw4l"">$VMUsedStorageInMB</td><tdclass=""tg-yw4l"">$VMBootGroupName</td><tdclass=""tg-yw4l"">$VMBootGroupDelay</td><tdclass=""tg-yw4l"">$VMJournalDatastoreClusterName</td><tdclass=""tg-yw4l"">$VMJournalDatastoreName</td><tdclass=""tg-yw4l"">$VMJournalHardLimitInMB</td><tdclass=""tg-yw4l"">$VMJournalHardLimitInPercent</td><tdclass=""tg-yw4l"">$VMDatastoreClusterName</td><tdclass=""tg-yw4l"">$VMDatastoreName</td><tdclass=""tg-yw4l"">$VMFolderName</td><tdclass=""tg-yw4l"">$VMHostClusterName</td><tdclass=""tg-yw4l"">$VMHostName</td></tr>"#Addingrowstotable$VMArrayHTMLTable+=$VMArrayHTMLTableRow}#CompilingEndofHTMLemail$VMArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$VMArrayHTMLTable=$VMArrayHTMLTableStart+$VMArrayHTMLTable+$VMArrayHTMLTableEnd$VMArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforVMVolumeArray################################################FunctionCreate-VMVolumeArrayTable{Param($Array,$TableCaption)$VMVolumeArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">PODName</th><thclass="tg-foxd">VPGName</th><thclass="tg-foxd">VMName</th><thclass="tg-foxd">VolumeID</th><thclass="tg-foxd">VolumeIsSWAP</th><thclass="tg-foxd">VolumeIsThin</th><thclass="tg-foxd">VolumeDatastoreClusterName</th><thclass="tg-foxd">VolumeDatastoreName</th></tr>"@#BuildingHTMLtable$VMVolumeArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$PODName=$_.SourcePOD

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 62 OF 134

$VPGName=$_.VPGName$VPGidentifier=$_.VPGidentifier$VMName=$_.VMName$VMIdentifier=$_.VMIdentifier$VMVolumeID=$_.VMVolumeID$VMVolumeIsSWAP=$_.VMVolumeIsSWAP$VMVolumeIsThin=$_.VMVolumeIsThin$VMVolumeDatastoreClusterName=$_.VMVolumeDatastoreClusterName$VMVolumeDatastoreClusterIdentifier=$_.VMVolumeDatastoreClusterIdentifier$VMVolumeDatastoreName=$_.VMVolumeDatastoreName$VMVolumeDatastoreIdentifier=$_.VMVolumeDatastoreIdentifier#BuildingHTMLtablerow$VMVolumeArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$PODName</td><tdclass=""tg-yw4l"">$VPGName</td><tdclass=""tg-yw4l"">$VMName</td><tdclass=""tg-yw4l"">$VMVolumeID</td><tdclass=""tg-yw4l"">$VMVolumeIsSWAP</td><tdclass=""tg-yw4l"">$VMVolumeIsThin</td><tdclass=""tg-yw4l"">$VMVolumeDatastoreClusterName</td><tdclass=""tg-yw4l"">$VMVolumeDatastoreName</td></tr>"#Addingrowstotable$VMVolumeArrayHTMLTable+=$VMVolumeArrayHTMLTableRow}#CompilingEndofHTMLemail$VMVolumeArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$VMVolumeArrayHTMLTable=$VMVolumeArrayHTMLTableStart+$VMVolumeArrayHTMLTable+$VMVolumeArrayHTMLTableEnd$VMVolumeArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforVMNICArray################################################FunctionCreate-VMNICArrayTable{Param($Array,$TableCaption)$VMNICArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">PODName</th><thclass="tg-foxd">VPGName</th><thclass="tg-foxd">VMName</th><thclass="tg-foxd">VMNICIdentifier</th><thclass="tg-foxd">FailoverNetworkName</th><thclass="tg-foxd">FailoverDNSSuffix</th><thclass="tg-foxd">FailoverShouldReplaceMacAddress</th><thclass="tg-foxd">FailoverGateway</th><thclass="tg-foxd">FailoverDHCP</th><thclass="tg-foxd">FailoverPrimaryDns</th><thclass="tg-foxd">FailoverSecondaryDns</th><thclass="tg-foxd">FailoverStaticIp</th><thclass="tg-foxd">FailoverSubnetMask</th><thclass="tg-foxd">FailoverTestNetworkName</th><thclass="tg-foxd">FailoverTestDNSSuffix</th><thclass="tg-foxd">FailoverTestShouldReplaceMacAddress</th><thclass="tg-foxd">FailoverTestGateway</th><thclass="tg-foxd">FailoverTestDHCP</th><thclass="tg-foxd">FailoverTestPrimaryDns</th><thclass="tg-foxd">FailoverTestSecondaryDns</th>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 63 OF 134

<thclass="tg-foxd">FailoverTestStaticIp</th><thclass="tg-foxd">FailoverTestSubnetMask</th></tr>"@#BuildingHTMLtable$VMNICArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$PODName=$_.SourcePOD$VPGName=$_.VPGName$VPGidentifier=$_.VPGidentifier$VMName=$_.VMName$VMNICIdentifier=$_.VMIdentifier$VMNICIdentifier=$_.VMNICIdentifier$VMNICFailoverNetworkName=$_.VMNICFailoverNetworkName$VMNICFailoverNetworkIdentifier=$_.VMNICFailoverNetworkIdentifier$VMNICFailoverDNSSuffix=$_.VMNICFailoverDNSSuffix$VMNICFailoverShouldReplaceMacAddress=$_.VMNICFailoverShouldReplaceMacAddress$VMNICFailoverGateway=$_.VMNICFailoverGateway$VMNICFailoverDHCP=$_.VMNICFailoverDHCP$VMNICFailoverPrimaryDns=$_.VMNICFailoverPrimaryDns$VMNICFailoverSecondaryDns=$_.VMNICFailoverSecondaryDns$VMNICFailoverStaticIp=$_.VMNICFailoverStaticIp$VMNICFailoverSubnetMask=$_.VMNICFailoverSubnetMask$VMNICFailoverTestNetworkName=$_.VMNICFailoverTestNetworkName$VMNICFailoverTestNetworkIdentifier=$_.VMNICFailoverTestNetworkIdentifier$VMNICFailoverTestDNSSuffix=$_.VMNICFailoverTestDNSSuffix$VMNICFailoverTestShouldReplaceMacAddress=$_.VMNICFailoverTestShouldReplaceMacAddress$VMNICFailoverTestGateway=$_.VMNICFailoverTestGateway$VMNICFailoverTestDHCP=$_.VMNICFailoverTestDHCP$VMNICFailoverTestPrimaryDns=$_.VMNICFailoverTestPrimaryDns$VMNICFailoverTestSecondaryDns=$_.VMNICFailoverTestSecondaryDns$VMNICFailoverTestStaticIp=$_.VMNICFailoverTestStaticIp$VMNICFailoverTestSubnetMask=$_.VMNICFailoverTestSubnetMask#BuildingHTMLtablerow$VMNICArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$PODName</td><tdclass=""tg-yw4l"">$VPGName</td><tdclass=""tg-yw4l"">$VMName</td><tdclass=""tg-yw4l"">$VMNICIdentifier</td><tdclass=""tg-yw4l"">$VMNICFailoverNetworkName</td><tdclass=""tg-yw4l"">$VMNICFailoverDNSSuffix</td><tdclass=""tg-yw4l"">$VMNICFailoverShouldReplaceMacAddress</td><tdclass=""tg-yw4l"">$VMNICFailoverGateway</td><tdclass=""tg-yw4l"">$VMNICFailoverDHCP</td><tdclass=""tg-yw4l"">$VMNICFailoverPrimaryDns</td><tdclass=""tg-yw4l"">$VMNICFailoverSecondaryDns</td><tdclass=""tg-yw4l"">$VMNICFailoverStaticIp</td><tdclass=""tg-yw4l"">$VMNICFailoverSubnetMask</td><tdclass=""tg-yw4l"">$VMNICFailoverTestNetworkName</td><tdclass=""tg-yw4l"">$VMNICFailoverTestDNSSuffix</td><tdclass=""tg-yw4l"">$VMNICFailoverTestShouldReplaceMacAddress</td><tdclass=""tg-yw4l"">$VMNICFailoverTestGateway</td><tdclass=""tg-yw4l"">$VMNICFailoverTestDHCP</td><tdclass=""tg-yw4l"">$VMNICFailoverTestPrimaryDns</td><tdclass=""tg-yw4l"">$VMNICFailoverTestSecondaryDns</td><tdclass=""tg-yw4l"">$VMNICFailoverTestStaticIp</td><tdclass=""tg-yw4l"">$VMNICFailoverTestSubnetMask</td></tr>"#Addingrowstotable$VMNICArrayHTMLTable+=$VMNICArrayHTMLTableRow}#CompilingEndofHTMLemail

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 64 OF 134

$VMNICArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$VMNICArrayHTMLTable=$VMNICArrayHTMLTableStart+$VMNICArrayHTMLTable+$VMNICArrayHTMLTableEnd$VMNICArrayHTMLTable#EndofTargetVRAArrayTablefunction}#################################################FunctionforbuildingHTMLtableforPODSummaryArray################################################FunctionCreate-PODSummaryArrayTable{Param($Array,$TableCaption)$PODSummaryArrayHTMLTableStart=@"<tableclass="tg"><caption><spanclass="caption">$TableCaption</span></caption><tr><thclass="tg-foxd">PODName</th><thclass="tg-foxd">VMs</th><thclass="tg-foxd">Protected</th><thclass="tg-foxd">UnProtected</th><thclass="tg-foxd">VPGs</th><thclass="tg-foxd">MeetingSLA</th><thclass="tg-foxd">NotMeetingSLA</th><thclass="tg-foxd">AverageRPO</th><thclass="tg-foxd">HighPriority</th><thclass="tg-foxd">MediumPriority</th><thclass="tg-foxd">LowPriority</th><thclass="tg-foxd">ProtectedSizeTB</th><thclass="tg-foxd">JournalSizeTB</th></tr>"@#BuildingHTMLtable$PODSummaryArrayHTMLTable=$nullforeach($_in$Array){#Settingvalues$PODName=$_.PODName$VMs=$_.VMs$VMsUnProtected=$_.VMsUnProtected$VMsProtected=$_.VMsProtected$VPGs=$_.VPGs$MeetingSLA=$_.MeetingSLA$NotMeetingSLA=$_.NotMeetingSLA$AverageRPO=$_.AverageRPO$HighPriority=$_.HighPriority$MediumPriority=$_.MediumPriority$LowPriority=$_.LowPriority$ProtectedSizeTB=$_.ProtectedSizeTB$JournalSizeTB=$_.JournalSizeTB#BuildingHTMLtablerow$PODSummaryArrayHTMLTableRow="<tr><tdclass=""tg-yw4l"">$PODName</td><tdclass=""tg-yw4l"">$VMs</td><tdclass=""tg-yw4l"">$VMsProtected</td><tdclass=""tg-yw4l"">$VMsUnProtected</td><tdclass=""tg-yw4l"">$VPGs</td><tdclass=""tg-yw4l"">$MeetingSLA</td><tdclass=""tg-yw4l"">$NotMeetingSLA</td><tdclass=""tg-yw4l"">$AverageRPO</td><tdclass=""tg-yw4l"">$HighPriority</td><tdclass=""tg-yw4l"">$MediumPriority</td><tdclass=""tg-yw4l"">$LowPriority</td><tdclass=""tg-yw4l"">$ProtectedSizeTB</td>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 65 OF 134

<tdclass=""tg-yw4l"">$JournalSizeTB</td></tr>"#Addingrowstotable$PODSummaryArrayHTMLTable+=$PODSummaryArrayHTMLTableRow}#CompilingEndofHTMLemail$PODSummaryArrayHTMLTableEnd=@"</table><br>"@#CompilingFinalHTML$PODSummaryArrayHTMLTable=$PODSummaryArrayHTMLTableStart+$PODSummaryArrayHTMLTable+$PODSummaryArrayHTMLTableEnd$PODSummaryArrayHTMLTable#EndofTargetVRAArrayTablefunction}#########################################################################################################################Customizereportsbelow##################################################################################################################################################################################################Building&SendingReport-PODSummaryReport##########################################################################SettingEmailsubject$Subject="ZertoPODSummaryReport"#CreatingTablesforEmailBody#Table1$PODSummaryArraySorted=$PODSummaryArray|Sort-ObjectPODName$PODSummaryArrayHTML=Create-PODSummaryArrayTable-Array$PODSummaryArraySorted-TableCaption"PODSummary"#Table2$VPGAlerts=$ProtectedVPGArray|Where-Object{$_.Status-ne"MeetingSLA"-or$_.RPOAlerts-ge"1"}|Sort-ObjectSourcePOD,VPGNameif($VPGAlertArraySorted-ne$null){$VPGAlertArrayHTML=Create-ProtectedVPGArrayTable-Array$VPGAlerts-TableCaption"AllVPGViolationsbyPODandVPGName"}else{$VPGAlertArrayHTML=$null}#Table2$TargetDatastoreAlerts=$TargetDatastoreArray|where-object{$_.UsedByZVR-eq"TRUE"-and$_.FreePercent-le"35"}|Sort-ObjectPODName,FreeSpaceGB#Onlycreatingtableifentriesexistif($TargetDatastoreAlerts-ne$null){$TargetDatastoreAlertHTML=Create-TargetDatastoreArrayTable-Array$TargetDatastoreAlerts-TableCaption"ZVRDatastoreswithlessthan35%FreeSpace"}else{$TargetDatastoreAlertHTML=$null}#Table3$ProtectedVPGArraySorted=$ProtectedVPGArray|Sort-ObjectSourcePOD,VPGName$ProtectedVPGArrayHTML=Create-ProtectedVPGArrayTable-Array$ProtectedVPGArraySorted-TableCaption"AllVPGsbyPODandVPGName"#Table4$ProtectedVMArraySorted=$ProtectedVMArray|Sort-ObjectSourcePOD,SourceCluster,VPGName,VMName$ProtectedVMArrayHTML=Create-ProtectedVMArrayTable-Array$ProtectedVMArraySorted-TableCaption"ProtectedVMsbyPOD,Cluster,VPGNameandVMName"#Table5$UnprotectedVMArraySorted=$UnprotectedVMArray|Sort-ObjectSourcePOD,VMFolder,VMName$UnprotectedVMArrayHTML=Create-UnprotectedVMArrayTable-Array$UnprotectedVMArraySorted-TableCaption"UnProtectedVMsbyPOD,FolderandVMName"#Table6$TargetVRAArraySorted=$TargetVRAArray|Sort-ObjectTargetPOD,VRAName$TargetVRAArrayHTML=Create-TargetVRAArrayTable-Array$TargetVRAArraySorted-TableCaption"VRAsbyTargetPODandVRAName"#Table7$TargetDatastoreArraySorted=$TargetDatastoreArray|where-object{$_.UsedByZVR-eq"TRUE"}|Sort-ObjectPODName,FreeSpaceGB$TargetDatastoreArrayHTML=Create-TargetDatastoreArrayTable-Array$TargetDatastoreArraySorted-TableCaption"AllZVRDatastoresbyPODandleastFreeSpace"#BuildingEmailBody$Body=$ReportHTMLTableStyle+$PODSummaryArrayHTML+$VPGAlertArrayHTML+$TargetDatastoreAlertHTML+$ProtectedVPGArrayHTML+$ProtectedVMArrayHTML+$UnprotectedVMArrayHTML+$TargetVRAArrayHTML+$TargetDatastoreArrayHTML

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 66 OF 134

#SavingCSVsofsortedarraystodisk,requiredtothenemail$EmailAttachment1=Save-CSV-Array$PODSummaryArraySorted-CSVFileName"PODSummaryArray"-CSVDirectory$CSVDirectory#$EmailAttachment2=Save-CSV-Array$VPGArray-CSVFileName"VPGArray2"-CSVDirectory$CSVDirectory#Combiningattachmentsifmultiplearerequired#$MultipleAttachments=@("$EmailAttachment1","$EmailAttachment2")#SendingtheemailEmail-ZVRReport-EmailTo$EmailList1-Subject$Subject-Body$Body-SMTPProfile$SMTPProfile1-Attachment$EmailAttachment1##########################################################################Building&SendingReport-VPGandVMSettingsReport##########################################################################SettingEmailsubject$Subject="ZertoVPGandVMSettingsReport"#CreatingTablesforEmailBody#VPGsettingstable$VPGArraySorted1=$VPGArray|Sort-ObjectPODName,VPGName$VPGArrayHTMLTable1=Create-VPGArrayTable-Array$VPGArraySorted1-TableCaption"VPGSettingsbyPODNameandVPGName"#VMsettingstable$VMArraySorted1=$VMArray|Sort-ObjectPODName,VPGName,VMName$VMArrayHTMLTable1=Create-VMArrayTable-Array$VMArraySorted1-TableCaption"VMSettingsbyPODName,VPGNameandVMName"#Volumesettingstable$VMVolumeArraySorted1=$VMVolumeArray|Sort-ObjectPODName,VPGName,VMName,VMVolumeID$VMVolumeArrayHTMLTable1=Create-VMVolumeArrayTable-Array$VMVolumeArraySorted1-TableCaption"VolumeSettingsbyPODName,VPGName,VMNameandVMVolumeID"#NICsettingstable$VMNICArraySorted1=$VMNICArray|Sort-ObjectPODName,VPGName,VMName,VMNICIdentifier$VMNICArrayHTMLTable1=Create-VMNICArrayTable-Array$VMNICArraySorted1-TableCaption"NICSettingsbyPODName,VPGName,VMNameandVMNICIdentifier"#BuildingEmailBody$Body=$ReportHTMLTableStyle+$VPGArrayHTMLTable1+$VMArrayHTMLTable1+$VMVolumeArrayHTMLTable1+$VMNICArrayHTMLTable1#SavingCSVsofsortedarraystodisk,requiredtothenemail$EmailAttachment1=Save-CSV-Array$VPGArraySorted1-CSVFileName"ZVRVPGSettings"-CSVDirectory$CSVDirectory$EmailAttachment2=Save-CSV-Array$VMArraySorted1-CSVFileName"ZVRVMSettings"-CSVDirectory$CSVDirectory$EmailAttachment3=Save-CSV-Array$VMVolumeArraySorted1-CSVFileName"ZVRVolumeSettings"-CSVDirectory$CSVDirectory$EmailAttachment4=Save-CSV-Array$VMNICArraySorted1-CSVFileName"ZVRNICSettings"-CSVDirectory$CSVDirectory#Combiningattachmentsifmultiplearerequired$MultipleAttachments=@("$EmailAttachment1","$EmailAttachment2","$EmailAttachment3","$EmailAttachment4")#SendingtheemailEmail-ZVRReport-EmailTo$EmailList1-Subject$Subject-Body$Body-SMTPProfile$SMTPProfile1-Attachment$MultipleAttachments

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 67 OF 134

5 AUTOMATING DEPLOYMENT

5.1 Use Cases

The simple interface of Zerto makes the initial deployment a very simple task. However, the number of clicks required can become cumbersome and error prone at scale when you are protecting hundreds to thousands of VMs across hundreds of hypervisor hosts. To resolve these challenges, the REST API can be utilized to not only automate the deployment of Virtual Replication Appliances (VRAs), but it can also be used to create the Virtual Protection Groups (VPGs). All examples given in this section of the document will include the full script due to the increased length of the examples. Comments are used throughout the examples to explain each component. When first testing automation scripts it is recommended to use non-production test VMs or hypervisor hosts. Powered-On Test VMs with no OS installed and a 1GB thin VMDK are ideal for VM protection automation testing due to the speed at which they are protected once a VPG is configured.

5.2 Bulk Automated VRA Deployment

To begin create a VRADeploymentESXiHosts.csv containing all the hosts and VRA settings to use for VRA deployment with the following column headings:

• ESXiHostName • DatastoreName • PortGroupName • VRAGroupName • MemoryInGB • DefaultGateway • SubnetMask • VRAIPAddress

Ensure that the names of ESXi hosts, datastores and port groups are all 100% correct otherwise the script will fail. VRA Groups should be configured per physical datacenter and so will usually match your vCenter name. Following is an example:

The CSV can now be utilized to deploy the VRAs one by one, by configuring the variables in the following script: #################################################Configurethevariablesbelow################################################$LogDataDir="C:\ZVRAPIBulkVRAScript\"$ESXiHostCSV="C:\ZVRAPIBulkVRAScript\VRADeploymentESXiHosts.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Password123"$SecondsBetweenVRADeployments="120"##################################################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 68 OF 134

#Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript###################################################################################################################################Settinglogdirectoryforengineandcurrentmonth################################################$CurrentMonth=get-date-formatMM.yy$CurrentTime=get-date-formathh.mm.ss$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\BulkVPGCreationLog-"+$CurrentTime+".txt"#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDirif($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#GetSiteIdentifierforgettingNetworkIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$SiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier$VRAInstallURL=$BaseURL+"vras"#################################################ImportingtheCSVofESXihoststodeployVRAto################################################$ESXiHostCSVImport=Import-Csv$ESXiHostCSV#################################################StartingInstallProcessforeachESXihostspecifiedintheCSV################################################foreach($ESXiHostin$ESXiHostCSVImport){#Settingvariablesforeaseofusethroughoutscript$VRAESXiHostName=$ESXiHost.ESXiHostName$VRADatastoreName=$ESXiHost.DatastoreName$VRAPortGroupName=$ESXiHost.PortGroupName

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 69 OF 134

$VRAGroupName=$ESXiHost.VRAGroupName$VRAMemoryInGB=$ESXiHost.MemoryInGB$VRADefaultGateway=$ESXiHost.DefaultGateway$VRASubnetMask=$ESXiHost.SubnetMask$VRAIPAddress=$ESXiHost.VRAIPAddress#GetNetworkIdentifierforAPI$APINetworkURL=$BaseURL+"virtualizationsites/$SiteIdentifier/networks"$APINetworkCMD=Invoke-RestMethod-Uri$APINetworkURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$NetworkIdentifier=$APINetworkCMD|Where-Object{$_.VirtualizationNetworkName-eq$VRAPortGroupName}|Select-ExpandPropertyNetworkIdentifier#GetHostIdentifierforAPI$APIHostURL=$BaseURL+"virtualizationsites/$SiteIdentifier/hosts"$APIHostCMD=Invoke-RestMethod-Uri$APIHostURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$VRAESXiHostID=$APIHostCMD|Where-Object{$_.VirtualizationHostName-eq$VRAESXiHostName}|Select-ExpandPropertyHostIdentifier#GetDatastoreIdentifierforAPI$APIDatastoreURL=$BaseURL+"virtualizationsites/$SiteIdentifier/datastores"$APIDatastoreCMD=Invoke-RestMethod-Uri$APIDatastoreURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$VRADatastoreID=$APIDatastoreCMD|Where-Object{$_.DatastoreName-eq$VRADatastoreName}|Select-ExpandPropertyDatastoreIdentifier#CreatingJSONBodyforAPIsettings$JSON="{""DatastoreIdentifier"":""$VRADatastoreID"",""GroupName"":""$VRAGroupName"",""HostIdentifier"":""$VRAESXiHostID"",""HostRootPassword"":null,""MemoryInGb"":""$VRAMemoryInGB"",""NetworkIdentifier"":""$NetworkIdentifier"",""UsePublicKeyInsteadOfCredentials"":true,""VraNetworkDataApi"":{""DefaultGateway"":""$VRADefaultGateway"",""SubnetMask"":""$VRASubnetMask"",""VraIPAddress"":""$VRAIPAddress"",""VraIPConfigurationTypeApi"":""Static""}}"write-host"Executing$JSON"#NowtryingAPIinstallcmdTry{Invoke-RestMethod-MethodPost-Uri$VRAInstallURL-Body$JSON-ContentType$TypeJSON-Headers$zertoSessionHeader}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#WaitingxxsecondsbeforedeployingthenextVRAwrite-host"Waiting$SecondsBetweenVRADeploymentssecondsbeforedeployingthenextVRAorstopping"sleep$SecondsBetweenVRADeployments#EndofperHostoperationsbelow}#EndofperHostoperationsabove#################################################Stoppinglogging################################################Stop-Transcript

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 70 OF 134

5.3 Bulk Automated VPG Creation – ZVM Only

This example is only for a Zerto Virtual Manager (ZVM) that is not managed by a Zerto Cloud Manager (ZCM) and therefore does not use Zerto Service Profiles. Bulk VM protection can be achieved using 2 CSV files. The first containing a list of Virtual Protection Groups (VPGs) with the VPG settings and the second a list of VMs to protect with their respective VPGs. VPG names must be unique, any duplicate entries will break the script. Friendly vSphere names are used for all the VPG settings which must match as they are shown in vCenter. For the VPG settings create a CSV with the following columns:

• VPGName • JournalHistoryInHours • ReplicationPriority • RecoverySiteName • RpoAlertInSeconds • TestIntervalInMinutes • ClusterName • FailoverNetwork • TestNetwork • DatastoreName • JournalDatastore • JournalHardLimitInMB • JournalWarningThresholdInMB • vCenterFolder

The RecoverySiteName is the target ZVM name and the default test interval 262080 shown below is recommended as per:

Now create a CSV containing the VM names, with respective VPGs, with the following columns:

• VMName • VPGName

The script supports multiple VMs per VPG, but keep in mind that in ZVR 4.5 a VM can only exist in 1 VPG at once when building out your list. Here you can see an example of this:

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 71 OF 134

Once both CSVs have been configured, set the variables at the top of the below script to automate bulk VM protection:

#################################################Configurethevariablesbelow################################################$LogDataDir="C:\ZVRBulkVMProtection\"$VPGList="C:\ZVRBulkVMProtection\ZVRBulkVMProtectionv1b-VPGs.csv"$VMList="C:\ZVRBulkVMProtection\ZVRBulkVMProtectionv1b-VMs.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"$TimeToWaitBetweenVPGCreation="120"#####################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#####################################################################################################################################################Settinglogdirectoryandstartingtranscriptlogging################################################$CurrentMonth=get-date-formatMM.yy$CurrentTime=get-date-formathh.mm.ss$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\BulkVPGCreationLog-"+$CurrentTime+".txt"#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDirif($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 72 OF 134

#ImportingtheCSVofProfilestouseforVMProtection################################################$VPGCSVImport=Import-Csv$VPGList$VMCSVImport=Import-Csv$VMList#################################################RunningthecreationprocessbyVPG,asaVPGcancontainmultipleVMs################################################foreach($VPGin$VPGCSVImport){$VPGName=$VPG.VPGName$ReplicationPriority=$VPG.ReplicationPriority$RecoverySiteName=$VPG.RecoverySiteName$ClusterName=$VPG.ClusterName$FailoverNetwork=$VPG.FailoverNetwork$TestNetwork=$VPG.TestNetwork$DatastoreName=$VPG.DatastoreName$JournalDatastore=$VPG.JournalDatastore$vCenterFolder=$VPG.vCenterFolder$JournalHistoryInHours=$VPG.JournalHistoryInHours$RpoAlertInSeconds=$VPG.RpoAlertInSeconds$TestIntervalInMinutes=$VPG.TestIntervalInMinutes$JournalHardLimitInMB=$VPG.JournalHardLimitInMB$JournalWarningThresholdInMB=$VPG.JournalWarningThresholdInMB#GettinglistofVMsfortheVPG$VPGVMs=$VMCSVImport|Where{$_.VPGName-Match"$VPGName"}$VPGVMNames=$VPGVMs.VMName#Loggingandshowingactionwrite-host"CreatingProtectionGroup:$VPGNameforVMs:$VPGVMNames"#################################################GettingIdentifiersforVPGsettings#################################################GetSiteIdentifierforgettingLocalIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#GetSiteIdentifierforgettingIdentifiers$TargetSiteInfoURL=$BaseURL+"virtualizationsites"$TargetSiteInfoCMD=Invoke-RestMethod-Uri$TargetSiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$TargetSiteIdentifier=$TargetSiteInfoCMD|Where-Object{$_.VirtualizationSiteName-eq$RecoverySiteName}|selectSiteIdentifier-ExpandPropertySiteIdentifier#GetNetworkIdentifiersforAPI$VISiteInfoURL1=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/networks"$VISiteInfoCMD1=Invoke-RestMethod-Uri$VISiteInfoURL1-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FailoverNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$FailoverNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier$TestNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$TestNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier#GetClusterIdentifierforAPI$VISiteInfoURL2=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/hostclusters"$VISiteInfoCMD2=Invoke-RestMethod-Uri$VISiteInfoURL2-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ClusterIdentifier=$VISiteInfoCMD2|Where-Object{$_.VirtualizationClusterName-eq$ClusterName}|SelectClusterIdentifier-ExpandPropertyClusterIdentifier#GetServiceProfileIdenfitiferforAPI$VISiteServiceProfileURL=$BaseURL+"serviceprofiles"$VISiteServiceProfileCMD=Invoke-RestMethod-Uri$VISiteServiceProfileURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ServiceProfileIdentifier=$VISiteServiceProfileCMD|Where-Object{$_.Description-eq$ServiceProfile}|SelectServiceProfileIdentifier-ExpandPropertyServiceProfileIdentifier#GetDatastoreIdentifiersforAPI$VISiteInfoURL3=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/datastores"$VISiteInfoCMD3=Invoke-RestMethod-Uri$VISiteInfoURL3-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$DatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$DatastoreName}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier$JournalDatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$JournalDatastore}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier#GetFoldersforAPI$VISiteInfoURL4=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/folders"$VISiteInfoCMD4=Invoke-RestMethod-Uri$VISiteInfoURL4-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FolderIdentifier=$VISiteInfoCMD4|Where-Object{$_.FolderName-eq$vCenterFolder}|SelectFolderIdentifier-ExpandPropertyFolderIdentifier#################################################GettingaVMidentifierforeachVMtobeprotectedandaddingittotheVMIDarray#################################################ResetingVMidentifierlist,requiredforcreatingmultipleprotectiongroups$VMIdentifierList=$null

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 73 OF 134

$VMIDArray=@()#RunningforeachVMoperationagainsttheVPGnameforeach($VMLinein$VPGVMNames){write-host"$VMLine"$VMInfoURL=$BaseURL+"virtualizationsites/$LocalSiteIdentifier/vms"$VMInfoCMD=Invoke-RestMethod-Uri$VMInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$VMIdentifier=$VMInfoCMD|Where-Object{$_.VmName-eq$VMLine}|selectVmIdentifier-ExpandPropertyVmIdentifier#AddingVMIDtoarray$VMIDArrayLine=new-objectPSObject$VMIDArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMID"-Value$VMIdentifier$VMIDArray+=$VMIDArrayLine}#################################################BuildingJSONRequestforpostingVPGsettingstoAPI################################################$JSONMain="{""Backup"":null,""Basic"":{""JournalHistoryInHours"":""$JournalHistoryInHours"",""Name"":""$VPGName"",""Priority"":""$ReplicationPriority"",""ProtectedSiteIdentifier"":""$LocalSiteIdentifier"",""RecoverySiteIdentifier"":""$TargetSiteIdentifier"",""RpoInSeconds"":""$RpoAlertInSeconds"",""ServiceProfileIdentifier"":null,""TestIntervalInMinutes"":""$TestIntervalInMinutes"",""UseWanCompression"":true,""ZorgIdentifier"":null},""BootGroups"":{""BootGroups"":[{""BootDelayInSeconds"":0,""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000000"",""Name"":""Default""}]},""Journal"":{""DatastoreClusterIdentifier"":null,""DatastoreIdentifier"":""$DatastoreIdentifier"",""Limitation"":{""HardLimitInMB"":""$JournalHardLimitInMB"",""HardLimitInPercent"":null,""WarningThresholdInMB"":""$JournalWarningThresholdInMB"",""WarningThresholdInPercent"":null}},""Networks"":{""Failover"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$FailoverNetworkIdentifier""}},""FailoverTest"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$TestNetworkIdentifier""}}},""Recovery"":{""DefaultDatastoreIdentifier"":""$DatastoreIdentifier"",""DefaultFolderIdentifier"":""$FolderIdentifier"",""DefaultHostClusterIdentifier"":""$ClusterIdentifier"",

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 74 OF 134

""DefaultHostIdentifier"":null,""ResourcePoolIdentifier"":null},""Scripting"":{""PostBackup"":null,""PostRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0},""PreRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0}},""Vms"":["#ResettingVMsifapreviousVPGwascreatedinthisrunofthescript$JSONVMs=$null#CreatingJSONrequestperVMusingtheVMarrayforalltheVMsintheVPGforeach($VMin$VMIDArray){$VMID=$VM.VMID$JSONVMsLine="{""VmIdentifier"":""$VMID""}"#RunningifstatementtocheckifthisisthefirstVMinthearray,ifnotthenacommaisaddedtostringif($JSONVMs-ne$null){$JSONVMsLine=","+$JSONVMsLine}$JSONVMs=$JSONVMs+$JSONVMsLine}#CreatingtheendoftheJSONrequest$JSONEnd="]}"#PuttingtheJSONrequestelementstogetherandoutputtingtherequest$JSON=$JSONMain+$JSONVMs+$JSONEndwrite-host"RunningJSONrequestbelow:$JSON"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeaderwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################ConfirmingVPGsettingsfromAPI################################################$ConfirmVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"$ConfirmVPGSettingCMD=Invoke-RestMethod-Uri$ConfirmVPGSettingURL-Headers$zertosessionHeader-ContentType$TypeJSON#################################################CommittingtheVPGsettingstobecreated################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitingVPGcreationforVPG:$VPGNamewithURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader-TimeoutSec100}Catch{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 75 OF 134

Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################Waiting$TimeToWaitBetweenVPGCreationsecondsbeforecreatingthenextVPG################################################write-host"Waiting$TimeToWaitBetweenVPGCreationsecondsbeforecreatingthenextVPGorstoppingscriptifonthelastVPG"sleep$TimeToWaitBetweenVPGCreation##EndofperVPGactionsbelow}#EndofperVPGactionsabove##################################################Stoppinglogging################################################Stop-Transcript

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 76 OF 134

5.4 Bulk Automated VPG Creation – ZVM & ZCM

This example is only for a Zerto Virtual Manager (ZVM) that is managed by a Zerto Cloud Manager (ZCM) and therefores requires the use Zerto Service Profiles in the REST API. Bulk VM protection can be achieved using 2 CSV files. The first containing a list of Virtual Protection Groups (VPGs) with the VPG settings and the second a list of VMs to protect with their respective VPGs. VPG names must be unique, any duplicate entries will break the script. Friendly vSphere names are used for all the VPG settings which must match as they are shown in vCenter. For the VPG settings create a CSV with the following columns:

• VPGName • ServiceProfile • ReplicationPriority • RecoverySiteName • ClusterName • FailoverNetwork • TestNetwork • DatastoreName • JournalDatastore • vCenterFolder

The RecoverySiteName is the target ZVM name and the Service Profile should be created in the ZCM in advance, it cannot be the service profile that already exists when the ZCM is deployed it must be a new profile as per the below example CSV:

Now create a CSV containing the VM names, with respective VPGs, with the following columns:

• VMName • VPGName

The script supports multiple VMs per VPG, but keep in mind that in ZVR 4.5 a VM can only exist in 1 VPG at once when building out your list. Here you can see an example of this:

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 77 OF 134

Once both CSVs have been configured, set the variables at the top of the below script to automate bulk VM protection: #################################################Configurethevariablesbelow################################################$LogDataDir="C:\ZVRBulkVMProtection\"$VPGList="C:\BulkVMProtection\ZVRBulkVMProtectionv1a-VPGs.csv"$VMList="C:\BulkVMProtection\ZVRBulkVMProtectionv1a-VMs.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"$TimeToWaitBetweenVPGCreation="120"#####################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#####################################################################################################################################################Settinglogdirectoryandstartingtranscriptlogging################################################$CurrentMonth=get-date-formatMM.yy$CurrentTime=get-date-formathh.mm.ss$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\BulkVPGCreationLog-"+$CurrentTime+".txt"#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDirif($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPIwithoutconnectingtovsphereusingPowerCLI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$ContentType#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 78 OF 134

#ImportingtheCSVofProfilestouseforVMProtection################################################$VPGCSVImport=Import-Csv$VPGList$VMCSVImport=Import-Csv$VMList#################################################RunningthecreationprocessbyVPG,asaVPGcancontainmultipleVMs################################################foreach($VPGin$VPGCSVImport){$VPGName=$VPG.VPGName$ServiceProfile=$VPG.ServiceProfile$ReplicationPriority=$VPG.ReplicationPriority$RecoverySiteName=$VPG.RecoverySiteName$ClusterName=$VPG.ClusterName$FailoverNetwork=$VPG.FailoverNetwork$TestNetwork=$VPG.TestNetwork$DatastoreName=$VPG.DatastoreName$JournalDatastore=$VPG.JournalDatastore$vCenterFolder=$VPG.vCenterFolder#GettinglistofVMsfortheVPG$VPGVMs=$VMCSVImport|Where{$_.VPGName-Match"$VPGName"}$VPGVMNames=$VPGVMs.VMName#Loggingandshowingactionwrite-host"CreatingProtectionGroup:$VPGNameforVMs:$VPGVMNames"#################################################GettingIdentifiersforVPGsettings#################################################GetSiteIdentifierforgettingLocalIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#GetSiteIdentifierforgettingIdentifiers$TargetSiteInfoURL=$BaseURL+"virtualizationsites"$TargetSiteInfoCMD=Invoke-RestMethod-Uri$TargetSiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$TargetSiteIdentifier=$TargetSiteInfoCMD|Where-Object{$_.VirtualizationSiteName-eq$RecoverySiteName}|selectSiteIdentifier-ExpandPropertySiteIdentifier#GetNetworkIdentifiersforAPI$VISiteInfoURL1=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/networks"$VISiteInfoCMD1=Invoke-RestMethod-Uri$VISiteInfoURL1-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$FailoverNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$FailoverNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier$TestNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$TestNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier#GetClusterIdentifierforAPI$VISiteInfoURL2=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/hostclusters"$VISiteInfoCMD2=Invoke-RestMethod-Uri$VISiteInfoURL2-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$ClusterIdentifier=$VISiteInfoCMD2|Where-Object{$_.VirtualizationClusterName-eq$ClusterName}|SelectClusterIdentifier-ExpandPropertyClusterIdentifier#GetServiceProfileIdenfitiferforAPI$VISiteServiceProfileURL=$BaseURL+"serviceprofiles"$VISiteServiceProfileCMD=Invoke-RestMethod-Uri$VISiteServiceProfileURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$ServiceProfileIdentifier=$VISiteServiceProfileCMD|Where-Object{$_.Description-eq$ServiceProfile}|SelectServiceProfileIdentifier-ExpandPropertyServiceProfileIdentifier#GetDatastoreIdentifiersforAPI$VISiteInfoURL3=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/datastores"$VISiteInfoCMD3=Invoke-RestMethod-Uri$VISiteInfoURL3-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$DatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$DatastoreName}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier$JournalDatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$JournalDatastore}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier#GetFoldersforAPI$VISiteInfoURL4=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/folders"$VISiteInfoCMD4=Invoke-RestMethod-Uri$VISiteInfoURL4-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$FolderIdentifier=$VISiteInfoCMD4|Where-Object{$_.FolderName-eq$vCenterFolder}|SelectFolderIdentifier-ExpandPropertyFolderIdentifier#################################################GettingaVMidentifierforeachVMtobeprotectedandaddingittotheVMIDarray#################################################ResetingVMidentifierlist,requiredforcreatingmultipleprotectiongroups$VMIdentifierList=$null$VMIDArray=@()#RunningforeachVMoperationagainsttheVPGnameforeach($VMLinein$VPGVMNames){

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 79 OF 134

write-host"$VMLine"$VMInfoURL=$BaseURL+"virtualizationsites/$LocalSiteIdentifier/vms"$VMInfoCMD=Invoke-RestMethod-Uri$VMInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$ContentType$VMIdentifier=$VMInfoCMD|Where-Object{$_.VmName-eq$VMLine}|selectVmIdentifier-ExpandPropertyVmIdentifier#AddingVMIDtoarray$VMIDArrayLine=new-objectPSObject$VMIDArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMID"-Value$VMIdentifier$VMIDArray+=$VMIDArrayLine}#################################################BuildingJSONRequestforpostingVPGsettingstoAPI################################################$JSONMain="{""Backup"":null,""Basic"":{""JournalHistoryInHours"":null,""Name"":""$VPGName"",""Priority"":""$ReplicationPriority"",""ProtectedSiteIdentifier"":""$LocalSiteIdentifier"",""RecoverySiteIdentifier"":""$TargetSiteIdentifier"",""RpoInSeconds"":null,""ServiceProfileIdentifier"":""$ServiceProfileIdentifier"",""TestIntervalInMinutes"":null,""UseWanCompression"":true,""ZorgIdentifier"":null},""BootGroups"":{""BootGroups"":[{""BootDelayInSeconds"":0,""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000000"",""Name"":""Default""}]},""Journal"":{""DatastoreClusterIdentifier"":null,""DatastoreIdentifier"":""$DatastoreIdentifier"",""Limitation"":{""HardLimitInMB"":null,""HardLimitInPercent"":null,""WarningThresholdInMB"":null,""WarningThresholdInPercent"":null}},""Networks"":{""Failover"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$FailoverNetworkIdentifier""}},""FailoverTest"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$TestNetworkIdentifier""}}},""Recovery"":{""DefaultDatastoreIdentifier"":""$DatastoreIdentifier"",""DefaultFolderIdentifier"":""$FolderIdentifier"",""DefaultHostClusterIdentifier"":""$ClusterIdentifier"",""DefaultHostIdentifier"":null,""ResourcePoolIdentifier"":null},""Scripting"":{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 80 OF 134

""PostBackup"":null,""PostRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0},""PreRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0}},""Vms"":["#ResettingVMsifapreviousVPGwascreatedinthisrunofthescript$JSONVMs=$null#CreatingJSONrequestperVMusingtheVMarrayforalltheVMsintheVPGforeach($VMin$VMIDArray){$VMID=$VM.VMID$JSONVMsLine="{""VmIdentifier"":""$VMID""}"#RunningifstatementtocheckifthisisthefirstVMinthearray,ifnotthenacommaisaddedtostringif($JSONVMs-ne$null){$JSONVMsLine=","+$JSONVMsLine}$JSONVMs=$JSONVMs+$JSONVMsLine}#CreatingtheendoftheJSONrequest$JSONEnd="]}"#PuttingtheJSONrequestelementstogetherandoutputtingtherequest$JSON=$JSONMain+$JSONVMs+$JSONEndwrite-host"RunningJSONrequestbelow:$JSON"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeaderwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################ConfirmingVPGsettingsfromAPI################################################$ConfirmVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"$ConfirmVPGSettingCMD=Invoke-RestMethod-Uri$ConfirmVPGSettingURL-Headers$zertosessionHeader-ContentType$TypeJSON#################################################CommittingtheVPGsettingstobecreated################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitingVPGcreationforVPG:$VPGNamewithURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader-TimeoutSec100}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 81 OF 134

#Waiting$TimeToWaitBetweenVPGCreationsecondsbeforecreatingthenextVPG################################################write-host"Waiting$TimeToWaitBetweenVPGCreationsecondsbeforecreatingthenextVPGorstoppingscriptifonthelastVPG"sleep$TimeToWaitBetweenVPGCreation##EndofperVPGactionsbelow}#EndofperVPGactionsabove##################################################Stoppinglogging################################################Stop-Transcript

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 82 OF 134

5.5 Bulk Automated VPG Creation with Boot Groups & Re-IP – ZVM Only

Bulk VM protection can be achieved using 2 CSV files. The first containing a list of Virtual Protection Groups (VPGs) with the VPG settings and the second a list of VMs to protect with their respective VPGs. VPG names must be unique, any duplicate entries will break the script. Friendly vSphere names are used for all the VPG settings which must match as they are shown in vCenter. This example also includes the ability to specify a VM boot group, 2 by default, and to automatically configure re-IP addressing on the 1st network adapter in the VM. For the VPG settings create a CSV with the following columns:

• VPGName • ReplicationPriority • RecoverySiteName • ClusterName • FailoverNetwork • TestNetwork • DatastoreName • JournalDatastore • vCenterFolder • BootGroupDelay

The RecoverySiteName is the target ZVM name and the Service Profile should be created in the ZCM in advance, it cannot be the service profile that already exists when the ZCM is deployed it must be a new profile as per the below example CSV:

Now create a CSV containing the VM names, with respective VPGs, with the following columns:

• VMName • VPGName • BootGroupName • VMNICFailoverNetworkName • VMNICFailoverDNSSuffix • VMNICFailoverShouldReplaceMacAddress • VMNICFailoverGateway • VMNICFailoverDHCP • VMNICFailoverPrimaryDns • VMNICFailoverSecondaryDns • VMNICFailoverStaticIp • VMNICFailoverSubnetMask • VMNICFailoverTestNetworkName • VMNICFailoverTestDNSSuffix • VMNICFailoverTestShouldReplaceMacAddress • VMNICFailoverTestGateway • VMNICFailoverTestDHCP

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 83 OF 134

• VMNICFailoverTestPrimaryDns • VMNICFailoverTestSecondaryDns • VMNICFailoverTestStaticIp • VMNICFailoverTestSubnetMask

The script supports multiple VMs per VPG, but keep in mind that in ZVR 4.5 a VM can only exist in 1 VPG at once when building out your list. Here you can see an example of this:

Once both CSVs have been configured, set the variables at the top of the below script to automate bulk VM protection:

#################################################Configurethevariablesbelow################################################$LogDataDir="C:\ZVRBulkVMProtection\"$VPGList="C:\BulkVMProtectionWithRe-IP\ZVRBulkVMProtectionvVPGs.csv"$VMList="C:\BulkVMProtectionWithRe-IP\ZVRBulkVMProtectionVMs.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"$TimeToWaitBetweenVPGCreation="120"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################################################################SettingCertPolicy-requiredforsuccessfulauthwiththeZertoAPIwithoutconnectingtovsphereusingPowerCLI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################ConnectingtovCenter-ifrequireduncommentlinebelow#################################################connect-viserver-Server$vCenterServer-User$vCenterUser-Password$vCenterPassword#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 84 OF 134

$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"#################################################ImportingtheCSVofProfilestouseforVMProtection################################################$VPGCSVImport=Import-Csv$VPGList$VMCSVImport=Import-Csv$VMList#################################################RunningthecreationprocessbyVPG,asaVPGcancontainmultipleVMs################################################foreach($VPGin$VPGCSVImport){$VPGName=$VPG.VPGName$ReplicationPriority=$VPG.ReplicationPriority$RecoverySiteName=$VPG.RecoverySiteName$ClusterName=$VPG.ClusterName$FailoverNetwork=$VPG.FailoverNetwork$TestNetwork=$VPG.TestNetwork$DatastoreName=$VPG.DatastoreName$JournalDatastore=$VPG.JournalDatastore$vCenterFolder=$VPG.vCenterFolder$JournalHistoryInHours=$VPG.JournalHistoryInHours$RpoAlertInSeconds=$VPG.RpoAlertInSeconds$TestIntervalInMinutes=$VPG.TestIntervalInMinutes$JournalHardLimitInMB=$VPG.JournalHardLimitInMB$JournalWarningThresholdInMB=$VPG.JournalWarningThresholdInMB$BootGroupDelay=$VPG.BootGroupDelay#GettinglistofVMsfortheVPG$VPGVMs=$VMCSVImport|Where{$_.VPGName-Match"$VPGName"}$VPGVMNames=$VPGVMs.VMName#Loggingandshowingactionwrite-host"CreatingProtectionGroup:$VPGNameforVMs:$VPGVMNames"#################################################GettingIdentifiersforVPGsettings#################################################GetSiteIdentifierforgettingLocalIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#GetSiteIdentifierforgettingIdentifiers$TargetSiteInfoURL=$BaseURL+"virtualizationsites"$TargetSiteInfoCMD=Invoke-RestMethod-Uri$TargetSiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$TargetSiteIdentifier=$TargetSiteInfoCMD|Where-Object{$_.VirtualizationSiteName-eq$RecoverySiteName}|selectSiteIdentifier-ExpandPropertySiteIdentifier#GettingVMidentifiers$VMInfoURL=$BaseURL+"virtualizationsites/$LocalSiteIdentifier/vms"$VMInfoCMD=Invoke-RestMethod-Uri$VMInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON#GetNetworkIdentifiersforAPI$VISiteInfoURL1=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/networks"$VISiteInfoCMD1=Invoke-RestMethod-Uri$VISiteInfoURL1-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FailoverNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$FailoverNetwork}|Select-ExpandPropertyNetworkIdentifier$TestNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$TestNetwork}|Select-ExpandPropertyNetworkIdentifier#GetClusterIdentifierforAPI$VISiteInfoURL2=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/hostclusters"$VISiteInfoCMD2=Invoke-RestMethod-Uri$VISiteInfoURL2-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ClusterIdentifier=$VISiteInfoCMD2|Where-Object{$_.VirtualizationClusterName-eq$ClusterName}|Select-ExpandPropertyClusterIdentifier#GetDatastoreIdentifiersforAPI$VISiteInfoURL3=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/datastores"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 85 OF 134

$VISiteInfoCMD3=Invoke-RestMethod-Uri$VISiteInfoURL3-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$DatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$DatastoreName}|Select-ExpandPropertyDatastoreIdentifier$JournalDatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$JournalDatastore}|Select-ExpandPropertyDatastoreIdentifier#GetFoldersforAPI$VISiteInfoURL4=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/folders"$VISiteInfoCMD4=Invoke-RestMethod-Uri$VISiteInfoURL4-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FolderIdentifier=$VISiteInfoCMD4|Where-Object{$_.FolderName-eq$vCenterFolder}|Select-ExpandPropertyFolderIdentifier#################################################BuildingJSONRequestforpostingVPGsettingstoAPI################################################$JSONMain="{""Backup"":null,""Basic"":{""JournalHistoryInHours"":""$JournalHistoryInHours"",""Name"":""$VPGName"",""Priority"":""$ReplicationPriority"",""ProtectedSiteIdentifier"":""$LocalSiteIdentifier"",""RecoverySiteIdentifier"":""$TargetSiteIdentifier"",""RpoInSeconds"":""$RpoAlertInSeconds"",""ServiceProfileIdentifier"":null,""TestIntervalInMinutes"":""$TestIntervalInMinutes"",""UseWanCompression"":true,""ZorgIdentifier"":null},""BootGroups"":{""BootGroups"":[{""BootDelayInSeconds"":""$BootGroupDelay"",""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000001"",""Name"":""Group1""},{""BootDelayInSeconds"":""0"",""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000002"",""Name"":""Group2""}]},""Journal"":{""DatastoreClusterIdentifier"":null,""DatastoreIdentifier"":""$DatastoreIdentifier"",""Limitation"":{""HardLimitInMB"":""$JournalHardLimitInMB"",""HardLimitInPercent"":null,""WarningThresholdInMB"":""$JournalWarningThresholdInMB"",""WarningThresholdInPercent"":null}},""Networks"":{""Failover"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$FailoverNetworkIdentifier""}},""FailoverTest"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$TestNetworkIdentifier""}}},""Recovery"":{""DefaultDatastoreIdentifier"":""$DatastoreIdentifier"",""DefaultFolderIdentifier"":""$FolderIdentifier"",""DefaultHostClusterIdentifier"":""$ClusterIdentifier"",""DefaultHostIdentifier"":null,

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 86 OF 134

""ResourcePoolIdentifier"":null},""Scripting"":{""PostBackup"":null,""PostRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0},""PreRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0}},""Vms"":["#ResettingVMsifapreviousVPGwascreatedinthisrunofthescript$JSONVMs=$null#CreatingJSONrequestperVMusingtheVMarrayforalltheVMsintheVPGforeach($VMin$VPGVMs){$VMName=$VM.VMName$BootGroupName=$VM.BootGroupName$VMSettings=$VMInfoCMD|Where-Object{$_.VmName-eq$VMName}|select*$VMIdentifier=$VMSettings|select-ExpandPropertyVmIdentifier#GettingVMNICsettings$VMNICFailoverNetworkName=$VM.VMNICFailoverNetworkName$VMNICFailoverDNSSuffix=$VM.VMNICFailoverDNSSuffix$VMNICFailoverShouldReplaceMacAddress=$VM.VMNICFailoverShouldReplaceMacAddress$VMNICFailoverGateway=$VM.VMNICFailoverGateway$VMNICFailoverDHCP=$VM.VMNICFailoverDHCP$VMNICFailoverPrimaryDns=$VM.VMNICFailoverPrimaryDns$VMNICFailoverSecondaryDns=$VM.VMNICFailoverSecondaryDns$VMNICFailoverStaticIp=$VM.VMNICFailoverStaticIp$VMNICFailoverSubnetMask=$VM.VMNICFailoverSubnetMask$VMNICFailoverTestNetworkName=$VM.VMNICFailoverTestNetworkName$VMNICFailoverTestDNSSuffix=$VM.VMNICFailoverTestDNSSuffix$VMNICFailoverTestShouldReplaceMacAddress=$VM.VMNICFailoverTestShouldReplaceMacAddress$VMNICFailoverTestGateway=$VM.VMNICFailoverTestGateway$VMNICFailoverTestDHCP=$VM.VMNICFailoverTestDHCP$VMNICFailoverTestPrimaryDns=$VM.VMNICFailoverTestPrimaryDns$VMNICFailoverTestSecondaryDns=$VM.VMNICFailoverTestSecondaryDns$VMNICFailoverTestStaticIp=$VM.VMNICFailoverTestStaticIp$VMNICFailoverTestSubnetMask=$VM.VMNICFailoverTestSubnetMask#SettinganswerstolowercaseforAPItoprocess$VMNICFailoverShouldReplaceMacAddress=$VMNICFailoverShouldReplaceMacAddress.ToLower()$VMNICFailoverDHCP=$VMNICFailoverDHCP.ToLower()$VMNICFailoverTestShouldReplaceMacAddress=$VMNICFailoverTestShouldReplaceMacAddress.ToLower()$VMNICFailoverTestDHCP=$VMNICFailoverTestDHCP.ToLower()#TranslatingnetworknamestoZVRNetworkIdentifiers$VMNICFailoverNetworkIdentifier=$VISiteInfoCMD1|where-object{$_.VirtualizationNetworkName-eq$VMNICFailoverNetworkName}|select-ExpandPropertyNetworkIdentifier$VMNICFailoverTestNetworkIdentifier=$VISiteInfoCMD1|where-object{$_.VirtualizationNetworkName-eq$VMNICFailoverTestNetworkName}|select-ExpandPropertyNetworkIdentifier#SettingbootgroupIDif($BootGroupName-eq"Group1"){$BootGroupIdentifier="00000000-0000-0000-0000-000000000001"}else{$BootGroupIdentifier="00000000-0000-0000-0000-000000000002"}######################BuildingJSONstart#####################$VMJSONStart="{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 87 OF 134

""BootGroupIdentifier"":""$BootGroupIdentifier"",""VmIdentifier"":""$VMIdentifier"",""Nics"":["######################BuildingNICJSON######################NICJSON$VMJSONNIC="{""Failover"":{""Hypervisor"":{""DnsSuffix"":""$VMNICFailoverDNSSuffix"",""IpConfig"":{""Gateway"":""$VMNICFailoverGateway"",""IsDhcp"":$VMNICFailoverDHCP,""PrimaryDns"":""$VMNICFailoverPrimaryDns"",""SecondaryDns"":""$VMNICFailoverSecondaryDns"",""StaticIp"":""$VMNICFailoverStaticIp"",""SubnetMask"":""$VMNICFailoverSubnetMask""},""NetworkIdentifier"":""$VMNICFailoverNetworkIdentifier"",""ShouldReplaceMacAddress"":$VMNICFailoverShouldReplaceMacAddress}},""FailoverTest"":{""Hypervisor"":{""DnsSuffix"":""$VMNICFailoverTestDNSSuffix"",""IpConfig"":{""Gateway"":""$VMNICFailoverTestGateway"",""IsDhcp"":$VMNICFailoverTestDHCP,""PrimaryDns"":""$VMNICFailoverTestPrimaryDns"",""SecondaryDns"":""$VMNICFailoverTestSecondaryDns"",""StaticIp"":""$VMNICFailoverTestStaticIp"",""SubnetMask"":""$VMNICFailoverTestSubnetMask""},""NetworkIdentifier"":""$VMNICFailoverTestNetworkIdentifier"",""ShouldReplaceMACAddress"":$VMNICFailoverTestShouldReplaceMacAddress}},""NicIdentifier"":""Networkadapter1""}"######################BuildingendofJSON#####################$VMJSONEnd="]}"######################PuttingJSONtogether#####################$JSONVMsLine=$VMJSONStart+$VMJSONNIC+$VMJSONEnd#RunningifstatementtocheckifthisisthefirstVMinthearray,ifnotthenacommaisaddedtostringif($JSONVMs-ne$null){$JSONVMsLine=","+$JSONVMsLine}$JSONVMs=$JSONVMs+$JSONVMsLine#EndofforeachVMbelow}#EndofforeachVMabove##CreatingtheendoftheJSONrequest$JSONEnd="],}"#PuttingtheJSONrequestelementstogetherandoutputtingtherequest$JSON=$JSONMain+$JSONVMs+$JSONEnd

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 88 OF 134

write-host"RunningJSONrequestbelow:$JSON"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeaderwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################CommittingtheVPGsettingstobecreated################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitingVPGcreationforVPG:$VPGNamewithURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader-TimeoutSec100}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################Waiting$TimeToWaitBetweenVPGCreationsecondsbeforecreatingthenextVPG################################################write-host"Waiting$TimeToWaitBetweenVPGCreationsecondsbeforecreatingthenextVPGorstoppingscriptifonthelastVPG"sleep$TimeToWaitBetweenVPGCreation##EndofperVPGactionsbelow}#EndofperVPGactionsabove

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 89 OF 134

6 AUTOMATING VM PROTECTION

6.1 Use Cases

The Zerto interface makes the protection of new VMs a very simple task using the create VPG workflow. This does however require manual processes to be followed for new VMs to be protected. In many large organizations where new VMs are being created daily this can lead to significant overheads. By leveraging the REST API and PowerShell, Zerto can be fully automated to protect new VMs to remove this burden and reduce the time to protection for new VMs and applications. Automated VM protection can be achieved both through PowerShell, or as part of a workflow in vRealize Orchestrator (vRO). This document will give examples of how to achieve both and the examples given will include the full script due to the increased length. Comments are used throughout the examples to explain each component. When first testing automation scripts it is recommended to use non-production test VMs. These should be Powered-On with no OS installed and a 1GB thin VMDK due to the speed at which they are protected once a VPG is configured.

6.2 Automating VM Protection by vSphere Folder - ZVM Only

This example is for a Zerto Virtual Manager (ZVM) that is not managed by a Zerto Cloud Manager (ZCM) and therefore does not use ZCM service profiles. The script can be used standalone or as part of vRealize Orchestrater workflow if desired. As the script interacts with vSphere folders, PowerCLI is a requirement of the examples given. To start, configure the below vSphere folders or define your own desired folder structure and configure Test VMs for testing initial protection:

In the above example VMs that require protection should be placed into the “ZVRVMsToProtect” subfolders of “ZVRBootGroup1” or “ZVRBootGroup2”. This can be done manually or using the vRO folder workflows depending on the desired start order of VMs. Once protected the VMs will be moved to the “ZVRProtectedVMs” folder.

The VPG name will be derived from the characters before the hyphen, multiple VMs per VPG and boot group are supported and the VMs will automatically be moved to the “ZVRProtectedVMs” once protected. In the above example this means that 2 VPGs will be created, Customer1 and Customer2 with 2 VMs in the first VPG and 1 VM in the second to demonstrate the functionality. Now configure a CSV with the required VPG configuration settings using the following column headings:

• ProfileNo • JournalHistoryInHours • ReplicationPriority • RecoverySiteName • RpoAlertInSeconds • TestIntervalInMinutes • ClusterName • FailoverNetwork • TestNetwork • DatastoreName • JournalDatastore

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 90 OF 134

• JournalHardLimitInMB • JournalWarningThresholdInMB • vCenterFolder • BootGroupDelay

The ProfileNo specified will be referenced at the start of the PowerShell script. Multiple scripts can be created to enable different VPG settings per workflow all from a single CSV containing multiple profiles. The RecoverySiteName is the target site ZVM name and columns for ClusterName, FailoverNetwork, TestNetwork, DatastoreName, JournalDatastore, vCenterFolder must match the friendly name of vSphere clusters, port groups for failover, test, datastore to store the VMs, journals and VM folder respectively. The BootGroupDelay is the number of seconds delay to insert between the 2 boot groups defined. Following is an example of the CSV configured:

To begin automating the VM protection configure the below variables in the following example PowerShell script: #################################################ConfigurethevariablesbelowusingtheProductionvCenter&ZVM################################################$LogDataDir="C:\ZVRAutomatedVMProtection\"$ProfileCSV="C:\ZVRAutomatedVMProtectionProfilesv1a.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"$vCenterServer="192.168.0.81"$vCenterUser="[email protected]"$vCenterPassword="Zerto1234!"$VPGProfileNo="1"$VMsToProtectvCenterFolderName="ZVRVMsToProtect"$ProtectedVMvCenterFolderName="ZVRProtectedVMs"$NextVPGCreationDelay="60"#####################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#####################################################################################################################################################Settinglogdirectoryforengineandcurrentmonth################################################$CurrentMonth=get-date-formatMM.yy$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentTime=get-date-formathh.mm.ss#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDir$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\VPGCreationLog-"+$CurrentTime+".txt"if($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################ConnectingtovCenter-requiredforsuccessfulauthenticationwithZertoAPI################################################connect-viserver-Server$vCenterServer-User$vCenterUser-Password$vCenterPassword#################################################BuildingZertoAPIstringandinvokingAPI################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 91 OF 134

$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"#################################################ImportingtheCSVofProfilestouseforVMProtection################################################$ProfileCSVImport=Import-Csv$ProfileCSV#################################################BuildinganArrayofallVMstoprotectfromthevSpherefolderandsettingthebootgroupID#################################################GettingalistofallVMs$VMsToProtect=get-vm*-Location$VMsToProtectvCenterFolderName|Select-ObjectName-ExpandPropertyName#GettingVMbootgroupinfo$VMBootGroup1List=get-vm*-Location"ZVRBootGroup1"|Select-ObjectName$VMBootGroup2List=get-vm*-Location"ZVRBootGroup2"|Select-ObjectName#SettingVMbootgroupIDs$VMBootGroup1ID="00000000-0000-0000-0000-000000000001"$VMBootGroup2ID="00000000-0000-0000-0000-000000000002"#CreatingTagarray$ZVRArray=@()#BuildingArrayofVMswithbootgroupsforeach($VMin$VMsToProtect){$CurrentVM=$VM.Name$VPGName=$CurrentVM-replace"-.*"#SettingVMbootgroupinfo$VMBootGroup1=$VMBootGroup1List|where{$_.Name-eq"$CurrentVM"}|Select-ObjectName-ExpandPropertyName$VMBootGroup2=$VMBootGroup2List|where{$_.Name-eq"$CurrentVM"}|Select-ObjectName-ExpandPropertyName#UsingIFstattementtosetcorrectbootgroupIDif($VMBootGroup1-ccontains$CurrentVM){$VMBootGroupID=$VMBootGroup1ID}if($VMBootGroup2-ccontains$CurrentVM){$VMBootGroupID=$VMBootGroup2ID}#CreatingArrayandaddinginfoforthecurrentVM$ZVRArrayLine=new-objectPSObject$ZVRArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$CurrentVM$ZVRArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$ZVRArrayLine|Add-Member-MemberTypeNoteProperty-Name"BootGroupID"-Value$VMBootGroupID$ZVRArray+=$ZVRArrayLine#EndofforeachVMbelow}#################################################LoadingtheVPGsettingsfromtheCSV,includingtheZertoServiceProfiletouse################################################$ProfileSettings=$ProfileCSVImport|where{$_.ProfileNo-eq"$VPGProfileNo"}$ReplicationPriority=$ProfileSettings.ReplicationPriority$RecoverySiteName=$ProfileSettings.RecoverySiteName$ClusterName=$ProfileSettings.ClusterName$FailoverNetwork=$ProfileSettings.FailoverNetwork

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 92 OF 134

$TestNetwork=$ProfileSettings.TestNetwork$DatastoreName=$ProfileSettings.DatastoreName$JournalDatastore=$ProfileSettings.JournalDatastore$vCenterFolder=$ProfileSettings.vCenterFolder$BootGroupDelay=$ProfileSettings.BootGroupDelay$JournalHistoryInHours=$ProfileSettings.JournalHistoryInHours$RpoAlertInSeconds=$ProfileSettings.RpoAlertInSeconds$TestIntervalInMinutes=$ProfileSettings.TestIntervalInMinutes$JournalHardLimitInMB=$ProfileSettings.JournalHardLimitInMB$JournalWarningThresholdInMB=$ProfileSettings.JournalWarningThresholdInMB#################################################CreatingListofVMstoProtectandprofilesettingsfromtheArraythenselectinguniqueVPGnames################################################$VPGsToCreate=$ZVRArray|selectVPGName-Unique#WritingoutputofVMstoprotectif($VMsToProtect-eq$null){write-host"NoVMsfoundtoprotectinvCenterfolder:$VMsToProtectvCenterFolderName"}else{#WritingoutputofVMstoprotectwrite-host"FoundthebelowVMsinthevCenterfoldertoprotect:$VMsToProtect"}#################################################RunningthecreationprocessbyVPGstocreatefromthe$VPGsToCreatevariable,asaVPGcancontainmultipleVMs################################################foreach($VPGin$VPGsToCreate){$VPGName=$VPG.VPGName$VPGVMs=$ZVRArray|Where{$_.VPGName-Match"$VPGName"}$VPGVMNames=$VPGVMs.VMName#NeedtogetZertoIdentifierforeachVMherewrite-host"CreatingProtectionGroup:$VPGNameforVMs:$VPGVMNames"#################################################GettingtheZertoVMIdentifiersforalltheVMstobecreatedinthisVPG#################################################GetSiteIdentifierforgettingLocalIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#ResetingVMidentifierlistandcreatingarray,neededasthiscouldbeexecutedformultipleVPGs$VMIdentifierList=$null$VMIDArray=@()#PerformingforeachVMtoprotectactionforeach($VMLinein$VPGVMNames){write-host"$VMLine"#GettingVMIDs$VMInfoURL=$BaseURL+"virtualizationsites/$LocalSiteIdentifier/vms"$VMInfoCMD=Invoke-RestMethod-Uri$VMInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$VMIdentifier=$VMInfoCMD|Where-Object{$_.VmName-eq$VMLine}|selectVmIdentifier-ExpandPropertyVmIdentifier$VMBootID=$ZVRArray|Where{$_.VMName-Match$VMLine}|Select-ObjectBootGroupID-ExpandPropertyBootGroupID#AddingVMIDandbootgrouptoarrayfortheAPI$VMIDArrayLine=new-objectPSObject$VMIDArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMID"-Value$VMIdentifier$VMIDArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMBootID"-Value$VMBootID$VMIDArray+=$VMIDArrayLine}#################################################GettingZertoidentifiersbasedonthefriendlynamesintheCSVtouseforVPGcreation#################################################GetSiteIdentifierforgettingIdentifiers$TargetSiteInfoURL=$BaseURL+"virtualizationsites"$TargetSiteInfoCMD=Invoke-RestMethod-Uri$TargetSiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 93 OF 134

$TargetSiteIdentifier=$TargetSiteInfoCMD|Where-Object{$_.VirtualizationSiteName-eq$RecoverySiteName}|selectSiteIdentifier-ExpandPropertySiteIdentifier#GetNetworkIdentifiersforAPI$VISiteInfoURL1=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/networks"$VISiteInfoCMD1=Invoke-RestMethod-Uri$VISiteInfoURL1-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FailoverNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$FailoverNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier$TestNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$TestNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier#GetClusterIdentifierforAPI$VISiteInfoURL2=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/hostclusters"$VISiteInfoCMD2=Invoke-RestMethod-Uri$VISiteInfoURL2-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ClusterIdentifier=$VISiteInfoCMD2|Where-Object{$_.VirtualizationClusterName-eq$ClusterName}|SelectClusterIdentifier-ExpandPropertyClusterIdentifier#GetServiceProfileIdenfitiferforAPI$VISiteServiceProfileURL=$BaseURL+"serviceprofiles"$VISiteServiceProfileCMD=Invoke-RestMethod-Uri$VISiteServiceProfileURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ServiceProfileIdentifier=$VISiteServiceProfileCMD|Where-Object{$_.Description-eq$ServiceProfile}|SelectServiceProfileIdentifier-ExpandPropertyServiceProfileIdentifier#GetDatastoreIdentifiersforAPI$VISiteInfoURL3=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/datastores"$VISiteInfoCMD3=Invoke-RestMethod-Uri$VISiteInfoURL3-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$DatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$DatastoreName}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier$JournalDatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$JournalDatastore}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier#GetFoldersforAPI$VISiteInfoURL4=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/folders"$VISiteInfoCMD4=Invoke-RestMethod-Uri$VISiteInfoURL4-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FolderIdentifier=$VISiteInfoCMD4|Where-Object{$_.FolderName-eq$vCenterFolder}|SelectFolderIdentifier-ExpandPropertyFolderIdentifier#OutputtingAPIresultsforeasiertroubleshootingwrite-host"ZVRAPIOutput:$TargetSiteInfoCMD$VISiteServiceProfileCMD$VISiteInfoCMD1$VISiteInfoCMD2$VISiteInfoCMD3$VISiteInfoCMD4"#################################################BuildingJSONRequestforpostingVPGsettingstoAPI################################################$JSONMain="{""Backup"":null,""Basic"":{""JournalHistoryInHours"":""$JournalHistoryInHours"",""Name"":""$VPGName"",""Priority"":""$ReplicationPriority"",""ProtectedSiteIdentifier"":""$LocalSiteIdentifier"",""RecoverySiteIdentifier"":""$TargetSiteIdentifier"",""RpoInSeconds"":""$RpoAlertInSeconds"",""ServiceProfileIdentifier"":null,""TestIntervalInMinutes"":""$TestIntervalInMinutes"",""UseWanCompression"":true,""ZorgIdentifier"":null},""BootGroups"":{""BootGroups"":[{""BootDelayInSeconds"":0,""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000001"",""Name"":""Database""},{""BootDelayInSeconds"":""$BootGroupDelay"",""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000002"",""Name"":""Web""}]},""Journal"":{""DatastoreClusterIdentifier"":null,""DatastoreIdentifier"":""$DatastoreIdentifier"",

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 94 OF 134

""Limitation"":{""HardLimitInMB"":""$JournalHardLimitInMB"",""HardLimitInPercent"":null,""WarningThresholdInMB"":""$JournalWarningThresholdInMB"",""WarningThresholdInPercent"":null}},""Networks"":{""Failover"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$FailoverNetworkIdentifier""}},""FailoverTest"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$TestNetworkIdentifier""}}},""Recovery"":{""DefaultDatastoreIdentifier"":""$DatastoreIdentifier"",""DefaultFolderIdentifier"":""$FolderIdentifier"",""DefaultHostClusterIdentifier"":""$ClusterIdentifier"",""DefaultHostIdentifier"":null,""ResourcePoolIdentifier"":null},""Scripting"":{""PostBackup"":null,""PostRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0},""PreRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0}},""Vms"":["#ResettingVMsifapreviousVPGwascreatedinthisrunofthescript$JSONVMs=$null#CreatingJSONVMarrayforalltheVMsintheVPGforeach($VMin$VMIDArray){$VMID=$VM.VMID$VMBootID=$VM.VMBootID$JSONVMsLine="{""VmIdentifier"":""$VMID"",""BootGroupIdentifier"":""$VMBootID""}"#RunningifstatementtocheckifthisisthefirstVMinthearray,ifnotthenacommaisaddedtostringif($JSONVMs-ne$null){$JSONVMsLine=","+$JSONVMsLine}$JSONVMs=$JSONVMs+$JSONVMsLine}#CreatingtheendoftheJSONrequest$JSONEnd="]}"#PuttingtheJSONrequesttogetherandoutputtingtherequest$JSON=$JSONMain+$JSONVMs+$JSONEndwrite-host"RunningJSONrequestbelow:$JSON"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 95 OF 134

{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeaderwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################ConfirmingVPGsettingsfromAPI################################################$ConfirmVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"$ConfirmVPGSettingCMD=Invoke-RestMethod-Uri$ConfirmVPGSettingURL-Headers$zertosessionHeader-ContentType$TypeJSON#################################################CommittingtheVPGsettingstobecreated################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitVPGSettingURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader-TimeoutSec100$VPGCreationStatus="PASSED"}Catch{$VPGCreationStatus="FAILED"Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################PerformingvSpherefolderchangeoperationtoindicateprotectedVM,onlyifsuccesfullyprotected################################################if($VPGCreationStatus-eq"PASSED"){foreach($_in$VPGVMNames){#SettingVMname$VMName=$_#ChangingVMtonewfolderwrite-host"MovingVM$VMNametoFolder$ProtectedVMvCenterFolderName"Move-VM-VM$VMName-Destination$ProtectedVMvCenterFolderName#EndofperVMfolderchangebelow}#EndofperVMfolderchangebelow##EndofperVMfolderactionifprotectionsucceededbelow}#EndofperVMfolderactionifprotectionsucceededabove##################################################Waitingxxminute/sbeforecreatingthenextVPG################################################write-host"Waiting$NextVPGCreationDelaysecondsbeforeprocessingnextVPGorfinishingscript"sleep$NextVPGCreationDelay#EndofperVPGactionsbelow}#EndofperVPGactionsabove#################################################DisconnectingfromvCenter################################################disconnect-viserver$vCenterServer-Force-Confirm:$false#################################################Stoppinglogging################################################stop-transcript

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 96 OF 134

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 97 OF 134

6.3 Automating VM Protection by vSphere Folder - ZVM & ZCM

This example is for a Zerto Virtual Manager (ZVM) that is managed by a Zerto Cloud Manager (ZCM) and therefore requires the use of ZCM service profiles. The script can be used standalone or with vRealize Orchestrater workflows if desired. As the script interacts with vSphere folders, PowerCLI is a requirement of the examples given. To start, configure the below vSphere folders or define your own folder structure and configure Test VMs for initial protection:

In the above example VMs that require protection should be placed into the “ZVRVMsToProtect” subfolders of “ZVRBootGroup1” or “ZVRBootGroup2”. This can be done manually or using the vRO folder workflows depending on the desired start order of VMs. Once protected the VMs will be moved to the “ZVRProtectedVMs” folder. The VPG name will be derived from the characters before the hyphen, multiple VMs per VPG and boot group are supported and the VMs will automatically be moved to the “ZVRProtectedVMs” once protected. In the above example this means that 2 VPGs will be created, Customer1 and Customer2 with 2 VMs in the first VPG and 1 VM in the second to demonstrate the functionality. Now configure a CSV with the required VPG configuration settings using the following column headings:

• ProfileNo • ZertoServiceProfile • ReplicationPriority • RecoverySiteName • ClusterName • FailoverNetwork • TestNetwork • DatastoreName • JournalDatastore • vCenterFolder • BootGroupDelay

The ProfileNo specified will be referenced at the start of the PowerShell script. Multiple scripts can be created to enable different VPG settings per workflow all from a single CSV containing multiple profiles. Each profile in a CSV must also reference a ZertoServiceProfile deployed from the ZCM. The Zerto Service Profile should be created in the ZCM in advance, it cannot be the service profile that already exists when the ZCM is deployed it must be a new profile. The RecoverySiteName is the target site ZVM name and columns for ClusterName, FailoverNetwork, TestNetwork, DatastoreName, JournalDatastore, vCenterFolder must match the friendly name of vSphere clusters, port groups for failover, test, datastore to store the VMs, journals and VM folder respectively. The BootGroupDelay is the number of seconds delay to insert between the 2 boot groups defined. Following is an example of the CSV configured:

To begin automating the VM protection configure the below variables in the following example PowerShell script:

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 98 OF 134

#################################################ConfigurethevariablesbelowusingtheProductionvCenter&ZVM################################################$LogDataDir="C:\ZVRAutomatedVMProtection\"$ProfileCSV="C:\ZVRAutomatedVMProtectionProfilesv1a.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"$vCenterServer="192.168.0.81"$vCenterUser="[email protected]"$vCenterPassword="Zerto1234!"$VPGProfileNo="1"$VMsToProtectvCenterFolderName="ZVRVMsToProtect"$ProtectedVMvCenterFolderName="ZVRProtectedVMs"$NextVPGCreationDelay="30"#####################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#####################################################################################################################################Settinglogdirectoryforengineandcurrentmonth################################################$CurrentMonth=get-date-formatMM.yy$CurrentLogDataDir=$LogDataDir+$CurrentMonth$CurrentTime=get-date-formathh.mm.ss#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDir$CurrentLogDataFile=$LogDataDir+$CurrentMonth+"\VPGCreationLog-"+$CurrentTime+".txt"if($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################ConnectingtovCenter-requiredforsuccessfulauthenticationwithZertoAPI################################################connect-viserver-Server$vCenterServer-User$vCenterUser-Password$vCenterPassword#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/JSON"$TypeXML="application/XML"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"#################################################ImportingtheCSVofProfilestouseforVMProtection################################################$ProfileCSVImport=Import-Csv$ProfileCSV#################################################BuildinganArrayofallVMstoprotectfromthevSpherefolderandsettingthebootgroupID#################################################GettingalistofallVMs

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 99 OF 134

$VMsToProtect=get-vm*-Location$VMsToProtectvCenterFolderName|Select-ObjectName-ExpandPropertyName#GettingVMbootgroupinfo$VMBootGroup1List=get-vm*-Location"ZVRBootGroup1"|Select-ObjectName$VMBootGroup2List=get-vm*-Location"ZVRBootGroup2"|Select-ObjectName#SettingVMbootgroupIDs$VMBootGroup1ID="00000000-0000-0000-0000-000000000001"$VMBootGroup2ID="00000000-0000-0000-0000-000000000002"#CreatingTagarray$ZVRArray=@()#BuildingArrayofVMswithbootgroupsforeach($VMin$VMsToProtect){$CurrentVM=$VM.Name$VPGName=$CurrentVM-replace"-.*"#SettingVMbootgroupinfo$VMBootGroup1=$VMBootGroup1List|where{$_.Name-eq"$CurrentVM"}|Select-ObjectName-ExpandPropertyName$VMBootGroup2=$VMBootGroup2List|where{$_.Name-eq"$CurrentVM"}|Select-ObjectName-ExpandPropertyName#UsingIFstattementtosetcorrectbootgroupIDif($VMBootGroup1-ccontains$CurrentVM){$VMBootGroupID=$VMBootGroup1ID}if($VMBootGroup2-ccontains$CurrentVM){$VMBootGroupID=$VMBootGroup2ID}#CreatingArrayandaddinginfoforthecurrentVM$ZVRArrayLine=new-objectPSObject$ZVRArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$CurrentVM$ZVRArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$ZVRArrayLine|Add-Member-MemberTypeNoteProperty-Name"BootGroupID"-Value$VMBootGroupID$ZVRArray+=$ZVRArrayLine#EndofforeachVMbelow}#################################################LoadingtheVPGsettingsfromtheCSV,includingtheZertoServiceProfiletouse################################################$ProfileSettings=$ProfileCSVImport|where{$_.ProfileNo-eq"$VPGProfileNo"}$ServiceProfile=$ProfileSettings.ZertoServiceProfile$ReplicationPriority=$ProfileSettings.ReplicationPriority$RecoverySiteName=$ProfileSettings.RecoverySiteName$ClusterName=$ProfileSettings.ClusterName$FailoverNetwork=$ProfileSettings.FailoverNetwork$TestNetwork=$ProfileSettings.TestNetwork$DatastoreName=$ProfileSettings.DatastoreName$JournalDatastore=$ProfileSettings.JournalDatastore$vCenterFolder=$ProfileSettings.vCenterFolder$BootGroupDelay=$ProfileSettings.BootGroupDelay#################################################CreatingListofVMstoProtectandprofilesettingsfromtheArraythenselectinguniqueVPGnames################################################$VPGsToCreate=$ZVRArray|selectVPGName-Unique#WritingoutputofVMstoprotectif($VMsToProtect-eq$null){write-host"NoVMsfoundtoprotectinvCenterfolder:$VMsToProtectvCenterFolderName"}else{#WritingoutputofVMstoprotectwrite-host"FoundthebelowVMsinthevCenterfoldertoprotect:$VMsToProtect"}#################################################RunningthecreationprocessbyVPGstocreatefromthe$VPGsToCreatevariable,asaVPGcancontainmultipleVMs################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 100 OF 134

foreach($VPGin$VPGsToCreate){$VPGName=$VPG.VPGName$VPGVMs=$ZVRArray|Where{$_.VPGName-Match"$VPGName"}$VPGVMNames=$VPGVMs.VMName#NeedtogetZertoIdentifierforeachVMherewrite-host"CreatingProtectionGroup:$VPGNameforVMs:$VPGVMNames"#################################################GettingtheZertoVMIdentifiersforalltheVMstobecreatedinthisVPG#################################################GetSiteIdentifierforgettingLocalIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#ResetingVMidentifierlistandcreatingarray,neededasthiscouldbeexecutedformultipleVPGs$VMIdentifierList=$null$VMIDArray=@()#PerformingforeachVMtoprotectactionforeach($VMLinein$VPGVMNames){write-host"$VMLine"#GettingVMIDs$VMInfoURL=$BaseURL+"virtualizationsites/$LocalSiteIdentifier/vms"$VMInfoCMD=Invoke-RestMethod-Uri$VMInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$VMIdentifier=$VMInfoCMD|Where-Object{$_.VmName-eq$VMLine}|selectVmIdentifier-ExpandPropertyVmIdentifier$VMBootID=$ZVRArray|Where{$_.VMName-Match$VMLine}|Select-ObjectBootGroupID-ExpandPropertyBootGroupID#AddingVMIDandbootgrouptoarrayfortheAPI$VMIDArrayLine=new-objectPSObject$VMIDArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMID"-Value$VMIdentifier$VMIDArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMBootID"-Value$VMBootID$VMIDArray+=$VMIDArrayLine}#################################################GettingZertoidentifiersbasedonthefriendlynamesintheCSVtouseforVPGcreation#################################################GetSiteIdentifierforgettingIdentifiers$TargetSiteInfoURL=$BaseURL+"virtualizationsites"$TargetSiteInfoCMD=Invoke-RestMethod-Uri$TargetSiteInfoURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$TargetSiteIdentifier=$TargetSiteInfoCMD|Where-Object{$_.VirtualizationSiteName-eq$RecoverySiteName}|selectSiteIdentifier-ExpandPropertySiteIdentifier#GetNetworkIdentifiersforAPI$VISiteInfoURL1=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/networks"$VISiteInfoCMD1=Invoke-RestMethod-Uri$VISiteInfoURL1-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FailoverNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$FailoverNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier$TestNetworkIdentifier=$VISiteInfoCMD1|Where-Object{$_.VirtualizationNetworkName-eq$TestNetwork}|SelectNetworkIdentifier-ExpandPropertyNetworkIdentifier#GetClusterIdentifierforAPI$VISiteInfoURL2=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/hostclusters"$VISiteInfoCMD2=Invoke-RestMethod-Uri$VISiteInfoURL2-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ClusterIdentifier=$VISiteInfoCMD2|Where-Object{$_.VirtualizationClusterName-eq$ClusterName}|SelectClusterIdentifier-ExpandPropertyClusterIdentifier#GetServiceProfileIdenfitiferforAPI$VISiteServiceProfileURL=$BaseURL+"serviceprofiles"$VISiteServiceProfileCMD=Invoke-RestMethod-Uri$VISiteServiceProfileURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$ServiceProfileIdentifier=$VISiteServiceProfileCMD|Where-Object{$_.Description-eq$ServiceProfile}|SelectServiceProfileIdentifier-ExpandPropertyServiceProfileIdentifier#GetDatastoreIdentifiersforAPI$VISiteInfoURL3=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/datastores"$VISiteInfoCMD3=Invoke-RestMethod-Uri$VISiteInfoURL3-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$DatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$DatastoreName}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier$JournalDatastoreIdentifier=$VISiteInfoCMD3|Where-Object{$_.DatastoreName-eq$JournalDatastore}|SelectDatastoreIdentifier-ExpandPropertyDatastoreIdentifier#GetFoldersforAPI$VISiteInfoURL4=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/folders"$VISiteInfoCMD4=Invoke-RestMethod-Uri$VISiteInfoURL4-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON$FolderIdentifier=$VISiteInfoCMD4|Where-Object{$_.FolderName-eq$vCenterFolder}|SelectFolderIdentifier-ExpandPropertyFolderIdentifier#OutputtingAPIresultsforeasiertroubleshootingwrite-host"ZVRAPIOutput:$TargetSiteInfoCMD$VISiteServiceProfileCMD$VISiteInfoCMD1

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 101 OF 134

$VISiteInfoCMD2$VISiteInfoCMD3$VISiteInfoCMD4"#DatastoreClustersforAPI-notusedinthisexample#$VISiteInfoURL5=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/datastoreclusters"#$VISiteInfoCMD5=Invoke-RestMethod-Uri$VISiteInfoURL5-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON#$DataStoreClusterIdentifier=$VISiteInfoCMD5|Where-Object{$_.FolderName-eq$vCenterFolder}|SelectFolderIdentifier-ExpandPropertyFolderIdentifier#GetHostIdentifierforAPI-notusedinthisexampleasusingtargetcluster(whichusessimpleroundrobin)#$VISiteHostURL=$BaseURL+"virtualizationsites/$TargetSiteIdentifier/hosts"#$VISiteHostCMD=Invoke-RestMethod-Uri$VISiteHostURL-TimeoutSec100-Headers$zertosessionHeader-ContentType$TypeJSON#$HostIdentifier=$VISiteHostCMD|Where-Object{$_.VirtualizationHostName-eq"Hostnamehere"}|SelectHostIdentifier-ExpandPropertyHostIdentifier#################################################BuildingJSONRequestforpostingVPGsettingstoAPI################################################$JSONMain="{""Backup"":null,""Basic"":{""JournalHistoryInHours"":null,""Name"":""$VPGName"",""Priority"":""$ReplicationPriority"",""ProtectedSiteIdentifier"":""$LocalSiteIdentifier"",""RecoverySiteIdentifier"":""$TargetSiteIdentifier"",""RpoInSeconds"":null,""ServiceProfileIdentifier"":""$ServiceProfileIdentifier"",""TestIntervalInMinutes"":null,""UseWanCompression"":true,""ZorgIdentifier"":null},""BootGroups"":{""BootGroups"":[{""BootDelayInSeconds"":0,""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000001"",""Name"":""Database""},{""BootDelayInSeconds"":""$BootGroupDelay"",""BootGroupIdentifier"":""00000000-0000-0000-0000-000000000002"",""Name"":""Web""}]},""Journal"":{""DatastoreClusterIdentifier"":null,""DatastoreIdentifier"":""$DatastoreIdentifier"",""Limitation"":{""HardLimitInMB"":null,""HardLimitInPercent"":null,""WarningThresholdInMB"":null,""WarningThresholdInPercent"":null}},""Networks"":{""Failover"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$FailoverNetworkIdentifier""}},""FailoverTest"":{""Hypervisor"":{""DefaultNetworkIdentifier"":""$TestNetworkIdentifier""}}},""Recovery"":{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 102 OF 134

""DefaultDatastoreIdentifier"":""$DatastoreIdentifier"",""DefaultFolderIdentifier"":""$FolderIdentifier"",""DefaultHostClusterIdentifier"":""$ClusterIdentifier"",""DefaultHostIdentifier"":null,""ResourcePoolIdentifier"":null},""Scripting"":{""PostBackup"":null,""PostRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0},""PreRecovery"":{""Command"":null,""Parameters"":null,""TimeoutInSeconds"":0}},""Vms"":["#ResettingVMsifapreviousVPGwascreatedinthisrunofthescript$JSONVMs=$null#CreatingJSONVMarrayforalltheVMsintheVPGforeach($VMin$VMIDArray){$VMID=$VM.VMID$VMBootID=$VM.VMBootID$JSONVMsLine="{""VmIdentifier"":""$VMID"",""BootGroupIdentifier"":""$VMBootID""}"#RunningifstatementtocheckifthisisthefirstVMinthearray,ifnotthenacommaisaddedtostringif($JSONVMs-ne$null){$JSONVMsLine=","+$JSONVMsLine}$JSONVMs=$JSONVMs+$JSONVMsLine}#CreatingtheendoftheJSONrequest$JSONEnd="]}"#PuttingtheJSONrequesttogetherandoutputtingtherequest$JSON=$JSONMain+$JSONVMs+$JSONEndwrite-host"RunningJSONrequestbelow:$JSON"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeaderwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################ConfirmingVPGsettingsfromAPI################################################$ConfirmVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"$ConfirmVPGSettingCMD=Invoke-RestMethod-Uri$ConfirmVPGSettingURL-Headers$zertosessionHeader-ContentType$TypeJSON#################################################CommittingtheVPGsettingstobecreated################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitVPGSettingURL:$CommitVPGSettingURL"Try{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 103 OF 134

Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader-TimeoutSec100$VPGCreationStatus="PASSED"}Catch{$VPGCreationStatus="FAILED"Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################PerformingvSpherefolderchangeoperationtoindicateprotectedVM,onlyifsuccesfullyprotected################################################if($VPGCreationStatus-eq"PASSED"){foreach($_in$VPGVMNames){#SettingVMname$VMName=$_#ChangingVMtonewfolderwrite-host"MovingVM$VMNametoFolder$ProtectedVMvCenterFolderName"Move-VM-VM$VMName-Destination$ProtectedVMvCenterFolderName#EndofperVMfolderchangebelow}#EndofperVMfolderchangebelow##EndofperVMfolderactionifprotectionsucceededbelow}#EndofperVMfolderactionifprotectionsucceededabove##################################################Waitingxxminute/sbeforecreatingthenextVPG################################################write-host"Waiting$NextVPGCreationDelaysecondsbeforeprocessingnextVPGorfinishingscript"sleep$NextVPGCreationDelay#EndofperVPGactionsbelow}#EndofperVPGactionsabove#################################################DisconnectingfromvCenter################################################disconnect-viserver$vCenterServer-Force-Confirm:$false#################################################Stoppinglogging################################################stop-transcript

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 104 OF 134

6.4 Automating VM Protection with vRealize Orchestrator

To integrate automated VM protection with vRealize Orchestrator (vRO) the appropriate script from sections 6.2 and 6.3 must first be built and tested as working. The scripts then need to be placed onto a PowerShell host in vRO. To configure a vRO host execute the “Add a PowerShell host” workflow in vRO:

Further information on other steps required, such as configuring Windows Remote Managed on the PowerShell host can be found on the following blog post: http://www.vmbaggum.nl/2015/04/how-to-add-a-powershell-host-to-vro/ The next step is to ensure the vRO workflows for VM creation place the VMs to protect in the specified vSphere folders in the correct naming convention, as per:

Remember, the VPG name is derived by all the characters before the hyphen in the VM name and multiple VMs are supported per VPG.

To execute automated VM protection add a “Invoke a PowerShell Script” into your VM creation workflow, referencing the automation script placed on the PowerShell host, and the integration is complete:

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 105 OF 134

6.5 Adding VMs to VPGs

When automating VM protection it might be required to add a VM to an existing VPG. The check to see if a VPG exists to then perform the correct operation is very simple: $VPGName = "DemoVPG1" $VPGURL = $baseURL+"vpgs" $VPGCMD = Invoke-RestMethod -Uri $VPGURL -TimeoutSec 100 -Headers $zertosessionHeader -ContentType "application/JSON" $VPGIdentifier = $SourceZVMVPGsCMD | Where-Object {$_.Vpgname -eq $VPGName} | Select-Object -ExpandProperty VpgIdentifier ################################################################################################ # IF VPG does EXIST, adding VMs to VPG ################################################################################################ if ($VPGIdentifier -ne $null) { write-host "VPG found - adding VM to VPG" } else { write-host "VPG not found - creating VPG" }

Once you have defined the need to add a VM to a VPG rather than create a new VPG, the following script can be used: #################################################ConfigurethevariablesbelowusingtheProductionvCenter&ZVM#################################################Uncommentthearguments&removestaticvariablestopassthesevaluestothescript#$VPGName=$args[0]#$VMName=$args[1]$VPGName="DemoVPG1"$VMName="NewVMtoAdd1"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader_JSON=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}$zertosessionHeader_XML=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeXML}#################################################GettingVPG&VMliststogetidentifiersofVPGandVMstoadd#################################################URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 106 OF 134

#VPGURLandList$vpgListApiUrl=$baseURL+"vpgs"$vpgList=Invoke-RestMethod-Uri$vpgListApiUrl-TimeoutSec100-Headers$zertoSessionHeader_XML-ContentType$TypeXML#BuildListofVMs$vmListApiUrl=$baseURL+"vms"$vmList=Invoke-RestMethod-Uri$vmListApiUrl-TimeoutSec100-Headers$zertoSessionHeader_JSON-ContentType$TypeJSON#SelectIDsfromtheAPIarray$zertoprotectiongrouparray=$vpgList.ArrayOfVpgApi.VpgApi|Select-ObjectOrganizationName,vpgname,vmscount,vpgidentifier$VPGIdentifier=$zertoprotectiongrouparray|Where-Object{$_.VpgName-eq$VPGName}|select-objectVpgIdentifier-ExpandPropertyVpgIdentifier#BuildingJSONforeditrequest$JSON="{""VpgIdentifier"":""$VPGidentifier""}"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeader_JSONwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################GettingtheZertoVMIdentifiersfortheVMtobecreatedinthisVPG#################################################GetSiteIdentifierforgettingLocalIdentifierlaterinthescript$SiteInfoURL=$BaseURL+"localsite"$SiteInfoCMD=Invoke-RestMethod-Uri$SiteInfoURL-TimeoutSec100-Headers$zertosessionHeader_JSON-ContentType$TypeJSON$LocalSiteIdentifier=$SiteInfoCMD|SelectSiteIdentifier-ExpandPropertySiteIdentifier#GettingVMidentifier$VMInfoURL=$BaseURL+"virtualizationsites/$LocalSiteIdentifier/vms"$VMInfoCMD=Invoke-RestMethod-Uri$VMInfoURL-TimeoutSec100-Headers$zertosessionHeader_JSON-ContentType$TypeJSON$VMIdentifier=$VMInfoCMD|Where-Object{$_.VmName-eq$VMName}|selectVmIdentifier-ExpandPropertyVmIdentifier#################################################BuildingURLs,JSONandPostingrequest################################################$AddVMURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/vms"$JSONVM="{""VmIdentifier"":""$vmIdentifier""}"Try{$AddVMPOST=Invoke-RestMethod-MethodPOST-Uri$AddVMURL-Body$JSONVM-ContentType$TypeJSON-Headers$zertosessionHeader_JSON-TimeoutSec100}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################CommittingtheVPGsettingstobecreated################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitVPGSettingURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader_JSON-TimeoutSec100$VPGCreationStatus="PASSED"}Catch{$VPGCreationStatus="FAILED"Write-Host$_.Exception.ToString()$error[0]|Format-List-Force

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 107 OF 134

}

7 BULK EDIT OPERATIONS

7.1 Bulk VPG Name Changing

In ZVR, VPG names are required to be unique and naming conventions can change. If you need to bulk change the name of the VPGs then scripting this procedure can save time and effot. Before importing the new names, you should first export the existing VPG names to a CSV using:

#################################################Configurethevariablesbelow################################################$ExportDataDir="C:\ZVRBulkVPGRename\"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################################################################SettingcertificateexceptiontopreventauthenticationissuestotheZVM################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#################################################BuildingarrayofVPGs################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 108 OF 134

$vpgListApiUrl=$baseURL+"vpgs"$vpgList=Invoke-RestMethod-Uri$vpgListApiUrl-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON$VPGs=$vpgList|selectVpgName,SourceSite,TargetSite,VmsCount,VpgIdentifier$VPGArray=@()#################################################BuildingarraylineperVPG################################################foreach($VPGin$VPGs){#Settingvalues$VpgName=$VPG.VpgName$SourceSite=$VPG.SourceSite$TargetSite=$VPG.TargetSite$VmsCount=$VPG.VmsCount$VPGidentifier=$VPG.VPGidentifier#Addinginfotoarray$VPGArrayLine=new-objectPSObject$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"OldVPGName"-Value$VPGName$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"NewVPGName"-Value""$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGidentifier"-Value$VPGidentifier$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"SourceSite"-Value$SourceSite$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"TargetSite"-Value$TargetSite$VPGArrayLine|Add-Member-MemberTypeNoteProperty-Name"VmsCount"-Value$VmsCount$VPGArray+=$VPGArrayLine}#################################################ExportingVPGnames################################################$VPGArray|export-csv$ExportDataDir"VPGNames.csv"-NoTypeInformation

This will then produce the below easy to edit CSV:

Configure the new VPG names, which must be unique, then run the below script to import the CSV and change the VPG names:

#################################################Configurethevariablesbelow################################################$CSVImportFile="C:\ZVRBulkVPGRename\VPGNames.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################ImportingCSVandbuildinglistofVPGs################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 109 OF 134

$VPGsToConfigure=Import-Csv$CSVImportFile#################################################SettingcertificateexceptiontopreventauthenticationissuestotheZVM################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtoeditVPGsettings$CreateVPGSettingsURL=$BaseURL+"vpgSettings"#################################################CheckingnoduplicatenewVPGnamesastheyhavetobeunique################################################$NewVPGNameCheck=$VPGsToConfigure.NewVPGName|Group-Object|Where-Object{$_.Count-gt1}if($NewVPGNameCheck-ne$null){write-host"DuplicateNewVPGnamefound,fixitandrunthescriptagain"sleep3exit}#################################################PerformingperVPGrenameaction################################################foreach($VPGin$VPGsToConfigure){$OldVPGName=$VPG.OldVPGName$NewVPGName=$VPG.NewVPGName$VPGidentifier=$VPG.VPGidentifier#################################################PostingtheVPGJSONRequesttotheAPItogetaVPGsettingsID#################################################BuildingJSONforeditrequest$JSONVPGSetting="{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 110 OF 134

""VpgIdentifier"":""$VPGidentifier""}"#PostingeditrequestTry{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGSettingsURL-Body$JSONVPGSetting-ContentType$TypeJSON-Headers$zertosessionHeaderwrite-host"VPGSettingsIdentifier:$VPGSettingsIdentifier"}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################ChangingtheVPGname################################################$JSON="{""Basic"":{""Name"":""$NewVPGName""}}"#PutURL&command$EditVPGURL=$BaseURL+"vpgsettings/"+$VPGSettingsIdentifierTry{$EditVPG=Invoke-RestMethod-MethodPUT-Uri$EditVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertosessionHeader}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#################################################CommittingtheVPGsetting################################################$CommitVPGSettingURL=$BaseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitVPGSettingURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-ContentType$TypeJSON-Headers$zertosessionHeader-TimeoutSec100write-host"SuccessfullychangedVPG:$OldVPGNametoVPG:$NewVPGName"}Catch{write-host"FailedchangingVPG:$OldVPGNametoVPG:$NewVPGName"}#SleepingbeforeprocessingnextVPGsleep5#EndofperVPGactionbelow}#EndofperVPGactionabove

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 111 OF 134

7.2 Bulk Editing VM NIC Settings Including Re-IP & Port Groups

Using the REST API, it is possible to configure new IP and MAC address settings using a CSV for simplicity, speed and to remove human errors by not having to configure each VM NIC manually. This same technique can also be used for changing Port Groups. To bulk change the VM NIC settings an export of the current VM NICs is required to build a CSV. This can be done with the below script:

#################################################Configurethevariablesbelow################################################$ExportDataDir="C:\ZVRBulkReIP\"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################################################################SettingcertificateexceptiontopreventauthenticationissuestotheZVM################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertoSessionHeader_JSON=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}$zertoSessionHeader_XML=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeXML}#################################################CreatingArraysforpopulatingZVMinfofromtheAPI################################################$VPGArray=@()$VMArray=@()$VMVolumeArray=@()

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 112 OF 134

$VMNICArray=@()#################################################CreatingVPGArray,VMArray,VMVolumeArray,VMNICArray#################################################URLtocreateVPGsettings$CreateVPGURL=$baseURL+"vpgSettings"#BuildListofVPGs$vpgListApiUrl=$baseURL+"vpgs"$vpgList=Invoke-RestMethod-Uri$vpgListApiUrl-TimeoutSec100-Headers$zertoSessionHeader_XML-ContentType$TypeXML#BuildListofVMs$vmListApiUrl=$baseURL+"vms"$vmList=Invoke-RestMethod-Uri$vmListApiUrl-TimeoutSec100-Headers$zertoSessionHeader_XML-ContentType$TypeXML#SelectIDsfromtheAPIarray$zertoprotectiongrouparray=$vpgList.ArrayOfVpgApi.VpgApi|Select-ObjectOrganizationName,vpgname,vmscount,vpgidentifier$vmListarray=$vmList.ArrayOfVmApi.VmApi|select-object*#################################################StartingforeachVPGactionofcollectingZVMVPGdata################################################foreach($VPGLinein$zertoprotectiongrouparray){$VPGidentifier=$VPGLine.vpgidentifier$VPGOrganization=$VPGLine.OrganizationName$VPGVMCount=$VPGLine.VmsCount$JSON="{""VpgIdentifier"":""$VPGidentifier""}"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertoSessionHeader_JSON$ValidVPGSettingsIdentifier=$true}Catch{$ValidVPGSettingsIdentifier=$false}#################################################GettingVPGsettingsfromAPI#################################################SkippingifunabletoobtainvalidVPGsettingidentifierif($ValidVPGSettingsIdentifier-eq$true){$VPGSettingsURL=$baseURL+"vpgSettings/"+$VPGSettingsIdentifier$VPGSettings=Invoke-RestMethod-Uri$VPGSettingsURL-Headers$zertoSessionHeader_JSON-ContentType$TypeJSON#GettingrecoverysiteID(neededanywayfornetworksettings)$VPGRecoverySiteIdentifier=$VPGSettings.Basic.RecoverySiteIdentifier#Gettingsiteinfo$VISitesURL=$baseURL+"virtualizationsites"$VISitesCMD=Invoke-RestMethod-Uri$VISitesURL-TimeoutSec100-Headers$zertoSessionHeader_JSON-ContentType$TypeJSON#Gettingnetworkinfo$VINetworksURL=$baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/networks"$VINetworksCMD=Invoke-RestMethod-Uri$VINetworksURL-TimeoutSec100-Headers$zertoSessionHeader_JSON-ContentType$TypeJSON#GettingVPGSettings$VPGName=$VPGSettings.Basic.Name#GettingVMIDsinVPG$VPGVMIdentifiers=$VPGSettings.VMs.VmIdentifier#################################################StartingforeachVMIDactionforcollectingZVMVMdata################################################foreach($_in$VPGVMIdentifiers){$VMIdentifier=$_#GetVMssettings$GetVMSettingsURL=$baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 113 OF 134

$GetVMSettings=Invoke-RestMethod-MethodGet-Uri$GetVMSettingsURL-TimeoutSec100-Headers$zertoSessionHeader_JSON-ContentType$TypeJSON#GettingtheVMnameanddiskusage$VMNameArray=$vmListarray|where-object{$_.VmIdentifier-eq$VMIdentifier}|Select-Object*$VMName=$VMNameArray.VmName#################################################GetVMNicsettingsforthecurrentVPG################################################$GetVMSettingNICsURL=$baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics"$GetVMSettingNICs=Invoke-RestMethod-MethodGet-Uri$GetVMSettingNICsURL-TimeoutSec100-Headers$zertoSessionHeader_XML-ContentType$TypeXML$VMNICIDs=$GetVMSettingNICs.ArrayOfVpgSettingsVmNicApi.VpgSettingsVmNicApi|select-objectNicIdentifier-ExpandPropertyNicIdentifier#################################################StartingforeachVMNICIDactionforcollectingZVMVMNICdata################################################foreach($_in$VMNICIDs){$VMNICIdentifier=$_$GetVMSettingNICURL=$baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics/"+$VMNICIdentifier$GetVMSettingNIC=Invoke-RestMethod-MethodGet-Uri$GetVMSettingNICURL-TimeoutSec100-Headers$zertoSessionHeader_XML-ContentType$TypeXML#Buildingarrays$VMSettingNICIDArray1=$GetVMSettingNIC.VpgSettingsVmNicApi.Failover.Hypervisor$VMSettingNICIDArray2=$GetVMSettingNIC.VpgSettingsVmNicApi.Failover.Hypervisor.IpConfig$VMSettingNICIDArray3=$GetVMSettingNIC.VpgSettingsVmNicApi.FailoverTest.Hypervisor$VMSettingNICIDArray4=$GetVMSettingNIC.VpgSettingsVmNicApi.FailoverTest.Hypervisor.IpConfig#Settingfailovervalues$VMNICFailoverDNSSuffix=$VMSettingNICIDArray1.DnsSuffix$VMNICFailoverNetworkIdentifier=$VMSettingNICIDArray1.NetworkIdentifier$VMNICFailoverShouldReplaceMacAddress=$VMSettingNICIDArray1.ShouldReplaceMacAddress$VMNICFailoverGateway=$VMSettingNICIDArray2.Gateway$VMNIsFailoverDHCP=$VMSettingNICIDArray2.IsDhcp$VMNICFailoverPrimaryDns=$VMSettingNICIDArray2.PrimaryDns$VMNICFailoverSecondaryDns=$VMSettingNICIDArray2.SecondaryDns$VMNICFailoverStaticIp=$VMSettingNICIDArray2.StaticIp$VMNICFailoverSubnetMask=$VMSettingNICIDArray2.SubnetMask#Nullingblankcontentif($VMNICFailoverDNSSuffix.nil-eq$true){$VMNICFailoverDNSSuffix=$null}if($VMNICFailoverGateway.nil-eq$true){$VMNICFailoverGateway=$null}if($VMNICFailoverPrimaryDns.nil-eq$true){$VMNICFailoverPrimaryDns=$null}if($VMNICFailoverSecondaryDns.nil-eq$true){$VMNICFailoverSecondaryDns=$null}if($VMNICFailoverStaticIp.nil-eq$true){$VMNICFailoverStaticIp=$null}if($VMNICFailoverSubnetMask.nil-eq$true){$VMNICFailoverSubnetMask=$null}#Settingfailovertestvalues$VMNICFailoverTestDNSSuffix=$VMSettingNICIDArray3.DnsSuffix$VMNICFailoverTestNetworkIdentifier=$VMSettingNICIDArray3.NetworkIdentifier$VMNICFailoverTestShouldReplaceMacAddress=$VMSettingNICIDArray3.ShouldReplaceMacAddress$VMNICFailoverTestGateway=$VMSettingNICIDArray4.Gateway$VMNIsFailoverTestDHCP=$VMSettingNICIDArray4.IsDhcp$VMNICFailoverTestPrimaryDns=$VMSettingNICIDArray4.PrimaryDns$VMNICFailoverTestSecondaryDns=$VMSettingNICIDArray4.SecondaryDns$VMNICFailoverTestStaticIp=$VMSettingNICIDArray4.StaticIp$VMNICFailoverTestSubnetMask=$VMSettingNICIDArray4.SubnetMask#Nullingblankcontentif($VMNICFailoverTestDNSSuffix.nil-eq$true){$VMNICFailoverTestDNSSuffix=$null}if($VMNICFailoverTestGateway.nil-eq$true){$VMNICFailoverTestGateway=$null}if($VMNICFailoverTestPrimaryDns.nil-eq$true){$VMNICFailoverTestPrimaryDns=$null}if($VMNICFailoverTestSecondaryDns.nil-eq$true){$VMNICFailoverTestSecondaryDns=$null}if($VMNICFailoverTestStaticIp.nil-eq$true){$VMNICFailoverTestStaticIp=$null}if($VMNICFailoverTestSubnetMask.nil-eq$true){$VMNICFailoverTestSubnetMask=$null}#MappingNetworkIDstoNames$VMNICFailoverNetworkName=$VINetworksCMD|Where-Object{$_.NetworkIdentifier-eq$VMNICFailoverNetworkIdentifier}|SelectVirtualizationNetworkName-ExpandPropertyVirtualizationNetworkName

$VMNICFailoverTestNetworkName=$VINetworksCMD|Where-Object{$_.NetworkIdentifier-eq$VMNICFailoverTestNetworkIdentifier}|SelectVirtualizationNetworkName-ExpandPropertyVirtualizationNetworkName#################################################AddingallVMNICsettinginfoto$VMNICArray################################################$VMNICArrayLine=new-objectPSObject$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPGName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGidentifier"-Value$VPGidentifier

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 114 OF 134

$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$VMName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMIdentifier"-Value$VMIdentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICIdentifier"-Value$VMNICIdentifier$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverNetworkName"-Value$VMNICFailoverNetworkName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverDNSSuffix"-Value$VMNICFailoverDNSSuffix$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverShouldReplaceMacAddress"-Value$VMNICFailoverShouldReplaceMacAddress$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverGateway"-Value$VMNICFailoverGateway$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverDHCP"-Value$VMNIsFailoverDHCP$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverPrimaryDns"-Value$VMNICFailoverPrimaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverSecondaryDns"-Value$VMNICFailoverSecondaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverStaticIp"-Value$VMNICFailoverStaticIp$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverSubnetMask"-Value$VMNICFailoverSubnetMask$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestNetworkName"-Value$VMNICFailoverTestNetworkName$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestDNSSuffix"-Value$VMNICFailoverTestDNSSuffix$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestShouldReplaceMacAddress"-Value$VMNICFailoverTestShouldReplaceMacAddress$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestGateway"-Value$VMNICFailoverTestGateway$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestDHCP"-Value$VMNIsFailoverTestDHCP$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestPrimaryDns"-Value$VMNICFailoverTestPrimaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestSecondaryDns"-Value$VMNICFailoverTestSecondaryDns$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestStaticIp"-Value$VMNICFailoverTestStaticIp$VMNICArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMNICFailoverTestSubnetMask"-Value$VMNICFailoverTestSubnetMask$VMNICArray+=$VMNICArrayLine#EndofperVMNICactionsbelow}#EndofperVMNICactionsabove##EndofperVMactionsbelow}#EndofperVMactionsabove#################################################DeletingVPGeditsettingsID(sameasclosingtheeditscreenonaVPGintheZVMwithoutmakinganychanges)################################################Try{Invoke-RestMethod-MethodDelete-Uri$VPGSettingsURL-TimeoutSec100-Headers$zertoSessionHeader_XML-ContentType$TypeXML}Catch[system.exception]{}##EndofcheckforvalidVPGsettingsIDbelow}#EndofcheckforvalidVPGsettingsIDabove##EndofperVPGactionsbelow}#EndofperVPGactionsabove##################################################ExportingVMNicSettings################################################$VMNICArray|export-csv$ExportDataDir"ZVRVMNICS.csv"-NoTypeInformation

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 115 OF 134

The following CSV will be created after running this script which can easily be edited with the new port group, IP and MAC address settings:

Once the CSV is configured run the below script to import the new settings: #################################################Configurethevariablesbelow################################################$CSVImportFile="C:\ZVRBulkReIP\ZVRVMNICS.csv"$ZertoServer="192.168.0.31"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################################################################ImportingCSVandbuildinglistofVPGs################################################$CSVImport=Import-Csv$CSVImportFile$VPGsToConfigure=$CSVImport|select-ExpandPropertyVPGName-Unique#################################################SettingcertificateexceptiontopreventauthenticationissuestotheZVM################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 116 OF 134

$TypeXML="application/xml"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertoSessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}$CreateVPGURL=$baseURL+"vpgSettings"#################################################StartingforeachVPGaction################################################foreach($VPGin$VPGsToConfigure){$VPGName=$VPG#GettingVPGIdentifier$VPGidentifier=$CSVImport|Where-Object{$_.VPGName-eq$VPGName}|select-ExpandPropertyVPGidentifier-Unique#GettinglistofVMstoreconfigure$VMsToConfigure=$CSVImport|Where-Object{$_.VPGName-eq$VPGName}|select-ExpandPropertyVMName-Unique#CreatingeditVPGJSON$JSON="{""VpgIdentifier"":""$VPGidentifier""}"#################################################PostingtheVPGJSONRequesttotheAPI################################################Try{$VPGSettingsIdentifier=Invoke-RestMethod-MethodPost-Uri$CreateVPGURL-Body$JSON-ContentType$TypeJSON-Headers$zertoSessionHeader$ValidVPGSettingsIdentifier=$true}Catch{$ValidVPGSettingsIdentifier=$false}#################################################SkippingifunabletoobtainvalidVPGsettingidentifier################################################if($ValidVPGSettingsIdentifier-eq$true){#################################################GettingZVRIDsfortheVPG################################################$VPGSettingsURL=$baseURL+"vpgSettings/"+$VPGSettingsIdentifier$VPGSettings=Invoke-RestMethod-Uri$VPGSettingsURL-Headers$zertoSessionHeader-ContentType$TypeJSON#GettingrecoverysiteID(neededanywayfornetworksettings)$VPGRecoverySiteIdentifier=$VPGSettings.Basic.RecoverySiteIdentifier#Gettingnetworkinfo$VINetworksURL=$baseURL+"virtualizationsites/$VPGRecoverySiteIdentifier/networks"$VINetworksCMD=Invoke-RestMethod-Uri$VINetworksURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON#################################################StartingperVMactions################################################foreach($VMin$VMsToConfigure){$VMName=$VM#GettingVMsettingsfromtheCSV$VMSettings=$CSVImport|Where-Object{$_.VPGName-eq$VPGName-and$_.VMName-eq$VMName}|select$VMIdentifier=$CSVImport|Where-Object{$_.VPGName-eq$VPGName-and$_.VMName-eq$VMName}|select-ExpandPropertyVMIdentifier-Unique$VMNICIdentifiers=$VMSettings.VMNICIdentifier######################StartingperVMNICactions

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 117 OF 134

#####################foreach($VMNICin$VMNICIdentifiers){$VMNICIdentifier=$VMNIC#GettingVMNICsettings$VMNICSettings=$VMSettings|Where-Object{$_.VMNICIdentifier-eq$VMNICIdentifier}|select*$VMNICFailoverNetworkName=$VMNICSettings.VMNICFailoverNetworkName$VMNICFailoverDNSSuffix=$VMNICSettings.VMNICFailoverDNSSuffix$VMNICFailoverShouldReplaceMacAddress=$VMNICSettings.VMNICFailoverShouldReplaceMacAddress$VMNICFailoverGateway=$VMNICSettings.VMNICFailoverGateway$VMNICFailoverDHCP=$VMNICSettings.VMNICFailoverDHCP$VMNICFailoverPrimaryDns=$VMNICSettings.VMNICFailoverPrimaryDns$VMNICFailoverSecondaryDns=$VMNICSettings.VMNICFailoverSecondaryDns$VMNICFailoverStaticIp=$VMNICSettings.VMNICFailoverStaticIp$VMNICFailoverSubnetMask=$VMNICSettings.VMNICFailoverSubnetMask$VMNICFailoverTestNetworkName=$VMNICSettings.VMNICFailoverTestNetworkName$VMNICFailoverTestDNSSuffix=$VMNICSettings.VMNICFailoverTestDNSSuffix$VMNICFailoverTestShouldReplaceMacAddress=$VMNICSettings.VMNICFailoverTestShouldReplaceMacAddress$VMNICFailoverTestGateway=$VMNICSettings.VMNICFailoverTestGateway$VMNICFailoverTestDHCP=$VMNICSettings.VMNICFailoverTestDHCP$VMNICFailoverTestPrimaryDns=$VMNICSettings.VMNICFailoverTestPrimaryDns$VMNICFailoverTestSecondaryDns=$VMNICSettings.VMNICFailoverTestSecondaryDns$VMNICFailoverTestStaticIp=$VMNICSettings.VMNICFailoverTestStaticIp$VMNICFailoverTestSubnetMask=$VMNICSettings.VMNICFailoverTestSubnetMask#SettinganswerstolowercaseforAPItoprocess$VMNICFailoverShouldReplaceMacAddress=$VMNICFailoverShouldReplaceMacAddress.ToLower()$VMNICFailoverDHCP=$VMNICFailoverDHCP.ToLower()$VMNICFailoverTestShouldReplaceMacAddress=$VMNICFailoverTestShouldReplaceMacAddress.ToLower()$VMNICFailoverTestDHCP=$VMNICFailoverTestDHCP.ToLower()#TranslatingnetworknamestoZVRNetworkIdentifiers$VMNICFailoverNetworkIdentifier=$VINetworksCMD|where-object{$_.VirtualizationNetworkName-eq$VMNICFailoverNetworkName}|select-ExpandPropertyNetworkIdentifier$VMNICFailoverTestNetworkIdentifier=$VINetworksCMD|where-object{$_.VirtualizationNetworkName-eq$VMNICFailoverTestNetworkName}|select-ExpandPropertyNetworkIdentifier######################BuildingVMNICJSON#####################$VMNICJSON="{""Failover"":{""Hypervisor"":{""DnsSuffix"":""$VMNICFailoverDNSSuffix"",""IpConfig"":{""Gateway"":""$VMNICFailoverGateway"",""IsDhcp"":$VMNICFailoverDHCP,""PrimaryDns"":""$VMNICFailoverPrimaryDns"",""SecondaryDns"":""$VMNICFailoverSecondaryDns"",""StaticIp"":""$VMNICFailoverStaticIp"",""SubnetMask"":""$VMNICFailoverSubnetMask""},""NetworkIdentifier"":""$VMNICFailoverNetworkIdentifier"",""ShouldReplaceMacAddress"":$VMNICFailoverShouldReplaceMacAddress}},""FailoverTest"":{""Hypervisor"":{""DnsSuffix"":""$VMNICFailoverTestDNSSuffix"",""IpConfig"":{""Gateway"":""$VMNICFailoverTestGateway"",""IsDhcp"":$VMNICFailoverTestDHCP,""PrimaryDns"":""$VMNICFailoverTestPrimaryDns"",""SecondaryDns"":""$VMNICFailoverTestSecondaryDns"",""StaticIp"":""$VMNICFailoverTestStaticIp"",""SubnetMask"":""$VMNICFailoverTestSubnetMask""},""NetworkIdentifier"":""$VMNICFailoverTestNetworkIdentifier"",""ShouldReplaceMACAddress"":$VMNICFailoverTestShouldReplaceMacAddress}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 118 OF 134

},""NicIdentifier"":""$VMNICIdentifier""}"######################CreatingURLandsendingPUTcommandtoAPI#####################$EditVMNICURL=$baseURL+"vpgSettings/"+$VPGSettingsIdentifier+"/vms/"+$VMIdentifier+"/nics/"+$VMNICIdentifierTry{$EditVMNIC=Invoke-RestMethod-MethodPUT-Uri$EditVMNICURL-Body$VMNICJSON-Headers$zertoSessionHeader-ContentType$TypeJSON-TimeoutSec100}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}#WaitingforAPIprocessingsleep3#EndofforeachVMNICbelow}#EndofforeachVMNICabove##EndofforeachVMbelow}#EndofforeachVMabove######################CommittingVPGsettings#####################$CommitVPGSettingURL=$baseURL+"vpgSettings/"+"$VPGSettingsIdentifier"+"/commit"write-host"CommitVPGSettingURL:$CommitVPGSettingURL"Try{Invoke-RestMethod-MethodPost-Uri$CommitVPGSettingURL-Headers$zertoSessionHeader-ContentType$TypeJSON-TimeoutSec100$VPGEditOutcome="PASSED"}Catch{$VPGEditOutcome="FAILED"Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}write-host"VPG:$VPGNameVPGEditOutcome=$VPGEditOutcome"#SleepingbeforeprocessingnextVPGwrite-host"Waiting5secondsbeforeprocessingnextVPG"sleep5#EndofcheckforvalidVPGsettingsIDbelow}#EndofcheckforvalidVPGsettingsIDabove##EndofperVPGactionsbelow}#EndofperVPGactionsabove

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 119 OF 134

8 SCHEDULING OFFSITE CLONES

8.1 Use Cases

To extend the retention of replica VM data beyond the journal it is possible to use offsite clones as a copy mechanism for taking point in time copies of each VPG. Using offsite clones has multiple advantages over utilizing offsite backups, these are:

• Clones are taken by the VRAs, not the ZVM • Increased throughput many times over by writing directly to ESXi datastores • Scale-out architecture enabling multiple simultaneous jobs • Protect many large VMs • Remove the need for VM-level backup solutions • Recover VMs by simply registering the VM in the inventory and powering on the VM • Recover VMs without a ZVM

8.2 Design Methodology

To schedule offsite clones this example script requires a combination of PowerCLI, Zerto PowerShell CMDlets and the Zerto REST API to fully automate scale-out offsite clone operations. Credentials are therefore required for both Zerto PowerShell authentication using the users.txt file on the ZVM along with vCenter and Zerto REST API server and credentials for the recovery site ZVM.

This script uses a CSV list to obtain the VPGs to clone along with the datastore on which to store the clones. Offsite clone operations are then run simulatenously using script blocks, 1 VPG at a time on each VRA, across all of the target site VRAs to maximize the throughput while minimizing the backup time and overheads.

I.E 10 VPGs replicating to 5 different VRAs = max 5 concurrent Offsite Clones & each VRA will clone 2 VPGs total, one after the other.

It is recommended to check balancing of VPGs to ensure the VPGs are evenly spread and the VMs in each VPG replicate to the same VRA to minimize the total clone time. The next offsite clone operation on each VRA will only commence when the last offsite clone on the VRA has finished. The script also has the following features:

• Email reports including clone time, size and throughput • Automatically remove VMs from the target vCenter inventory (configured as variable in script) • Use VSS checkpoint with the option to fail if no VSS checkpoint found

For storing the offsite clones, it is recommended to use deduplicated storage to minimize the storage overheads of multiple data copies. The storage device can be a dedicated physical appliance or Windows Server storage presented to the ESXi hosts using NFS, see the example below:

http://www.lazywinadmin.com/2013/01/configure-windows-server-2012-nfs.html

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 120 OF 134

8.3 Scheduled Offsite Clone

Following is the example script to schedule offsite clones. Key variables to configure are vCenterTimeZoneMatch, to enable accurate reporting of clone completion times, and ResourceReportHourlySample which should be configured to match the Resource Report sample rate of the recovery site ZVM which should be set as the ZertoServer variable.

#################################################Configurethevariablesbelow#################################################VPGstoclone$OffsiteCloneVPGsCSV="C:\OffsiteCloneVPGs.csv"#Loggingdirectories$OffsiteCloneLogDataDir="C:\ZVROffsiteCloning\"$OffsiteCloneVPGLog="C:\ZVROffsiteCloning\OffsiteCloneVPGLog.csv"$OffsiteCloneVMLog="C:\ZVROffsiteCloning\OffsiteCloneVMLog.csv"#vCenterlogininfo$vCenterServer="192.168.0.82"$vCenterUser="[email protected]"$vCenterPassword="Zerto1234!"#PowerShelllogininfo,canuseeitherZVM$ZVMIP="192.168.0.32"$ZVMPSPort="9080"$ZMVPSUser="administrator"$ZVMPSPasswd="password"#APIloginInfo,mustuserecoverysiteZVMforResourceReportAPI$ZertoServer="192.168.0.32"$ZertoPort="9669"$ZertoUser="[email protected]"$ZertoPassword="Zerto1234!"#RemoveVMfrominventorytoggleTRUE/FALSE$RemoveVMsFromInventory="TRUE"#Configuretheemailsettings$EmailTo="[email protected]"$EmailFrom="[email protected]"$SMTPServer="localhost"$SMTPPort="25"$SMTPUser="[email protected]"$SMTPPassword="Srt1234!"$SMTPSSLEnabled="FALSE"#vCentertimezoneandPowerShellscripthosttimezonematch#IfdisabledtheVMregistrationtimeisoverwrittenbythetimetheVMwasfound,lessaccurate,butremovestimezoneissues$vCenterTimeZoneMatch="FALSE"#Configureresourcereportsamplingrate,bydefaultdaily,ifleftasdailythensetthebelowtofalse$ResourceReportHourlySample="TRUE"#########################################################################################################################Nothingtoconfigurebelowthisline-Startingthemainfunctionofthescript#########################################################################################################################################################################SettingZVMsecurityexceptionforRESTAPI################################################add-type@"usingSystem.Net;usingSystem.Security.Cryptography.X509Certificates;publicclassTrustAllCertsPolicy:ICertificatePolicy{publicboolCheckValidationResult(ServicePointsrvPoint,X509Certificatecertificate,WebRequestrequest,intcertificateProblem){returntrue;}}"@[System.Net.ServicePointManager]::CertificatePolicy=New-ObjectTrustAllCertsPolicy#################################################BuildingZertoAPIstringandinvokingAPI################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 121 OF 134

$baseURL="https://"+$ZertoServer+":"+$ZertoPort+"/v1/"#AuthenticatingwithZertoAPIs$xZertoSessionURL=$baseURL+"session/add"$authInfo=("{0}:{1}"-f$ZertoUser,$ZertoPassword)$authInfo=[System.Text.Encoding]::UTF8.GetBytes($authInfo)$authInfo=[System.Convert]::ToBase64String($authInfo)$headers=@{Authorization=("Basic{0}"-f$authInfo)}$sessionBody='{"AuthenticationMethod":"1"}'$TypeJSON="application/json"$TypeXML="application/xml"Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON}Catch[system.exception]{}Finally{}#Extractingx-zerto-sessionfromtheresponse,andaddingittotheactualAPI$xZertoSession=$xZertoSessionResponse.headers.get_item("x-zerto-session")$zertosessionHeader=@{"x-zerto-session"=$xZertoSession;"Accept"=$TypeJSON}#URLtocreateVPGsettings$CreateVPGURL=$BaseURL+"vpgSettings"#VPGURLandList$vpgListApiUrl=$baseURL+"vpgs"$vpgList=Invoke-RestMethod-Uri$vpgListApiUrl-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON#BuildListofVMs$vmListApiUrl=$baseURL+"vms"$vmList=Invoke-RestMethod-Uri$vmListApiUrl-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON#BuildListofVRAs$vraListApiUrl=$baseURL+"vras"$vraList=Invoke-RestMethod-Uri$vraListApiUrl-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON#Settingresourcereportvaluesbasedon$ResourceReportHourlySampleif($ResourceReportHourlySample-eq"TRUE"){$NowDateTime=get-date-Format"yyyy-MM-ddHH:mm:ss"$ThenDateTime=(get-date).AddHours(-1).ToString("yyyy-MM-ddHH:mm:ss")}else{$StartDateTime=get-date-Format"yyyy-MM-dd"$EndDateTime=(get-date).AddDays(1).ToString("yyyy-MM-dd")}#QueryResourceReportwithentriesfromthelasthour$ResourceReportURL="https://"+$ZertoServer+":"+$ZertoPort+"/ZvmService/ResourcesReport/getSamples?fromTimeString="+$ThenDateTime+"&toTimeString="+$NowDateTime+"&startIndex=0&count=500"$ResourceReport=Invoke-RestMethod-Uri$ResourceReportURL-TimeoutSec100-Headers$zertoSessionHeader-ContentType$TypeJSON#ImportingCSVforVPGs$OffsiteCloneVPGs=import-csv$OffsiteCloneVPGsCSV$OffsiteCloneVPGsByName=$OffsiteCloneVPGs|Select-ObjectVPGName-ExpandPropertyVPGName#BuildinglistofVRAsthathaveVPGstoclone$TargetSiteVPGs=$ResourceReport|Sort-ObjectVPGName-Unique|SelectVPGName,TargetVraName#################################################BuildingarrayofVRAsthathaveVPGsthatrequireoffsiteclone################################################$TargetVRAArray=@()foreach($VPGin$TargetSiteVPGs){if($OffsiteCloneVPGsByName-ccontains$VPG.VpgName){$TargetVRAArrayLine=new-objectPSObject$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGName"-Value$VPG.VpgName$TargetVRAArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRAName"-Value$VPG.TargetVraName$TargetVRAArray+=$TargetVRAArrayLine}

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 122 OF 134

}$TargetSiteVRAs=$TargetVRAArray|Select-ObjectVRAName-Unique#################################################################################################PerformingforeachVRAthathasanoffsiteclonetorunaction################################################################################################foreach($VRAin$TargetSiteVRAs){$VRAName=$VRA.VRAName#GettingtheVPGstocloneforthecurrentVPG$VRAVPGsToClone=$null$VRAVPGsToClone=$TargetVRAArray|where-object{$_.VRAName-eq"$VRAName"}|Select-ObjectVPGName-ExpandPropertyVPGName-Uniquewrite-host"$VRANameVPGstoclone:$VRAVPGsToClone"$Now=get-date$JobTime=$Now.ToString("yyy-MM-dd_HH-mm-ss")#################################################ScriptblockwithinperVRA################################################$VRAScriptBlock={param($VRAName,$VRAVPGsToClone,$vmList,$OffsiteCloneVPGs,$vCenterServer,$vCenterUser,$vCenterPassword,$ZVMIP,$ZVMPSPort,$ZMVPSUser,$ZVMPSPasswd,$OffsiteCloneVPGLog,$OffsiteCloneVMLog,$OffsiteCloneLogDataDir,$RemoveVMsFromInventory,$vCenterTimeZoneMatch,$EmailTo,$EmailFrom,$SMTPServer,$SMTPPort,$SMTPUser,$SMTPPassword,$SMTPSSLEnabled

)#################################################CreatingfunctiontogetVMregistereddatetime################################################functionGet-VMEvents{<#.SynopsisGeteventsforanentityorforqueryallevents..DescriptionThisfunctionreturnseventsforentities.It'sverysimilartoget-vieventcmdlet.Notethatget-VMEventcanhandle1vmatatime.Youcannotsendarrayofvmsinthisversionofthescript..ExampleGet-VMEvents0All-types"VmCreatedEvent","VmDeployedEvent","VmClonedEvent"ThiswillreceiveALLeventsoftypes"VmCreatedEvent","VmDeployedEvent","VmClonedEvent"..ExampleGet-VMEvents-name'vm1'-types"VmCreatedEvent"Willouputcreationeventsforvm:'vm1'.Thiswasisfasterthanpipingvmsfromget-vmresult.Thereisnoneedtouseget-vmtopassnamestoget-vmevents.Still,itisokwhenyouwilldoit,itwillmakeitjustalittlebitslower .ExampleGet-VMEvents-name'vm1'-category'warning'Willouputalleventsforvm:'vm1'.Thiswasisfasterthanpipingnamesfromget-vmcmdlet.Categorywillmakeget-vmeventtosearchonlydefinedcategoryevents..Exampleget-vm'vm1'|Get-VMEvents-types"VmCreatedEvent","VmMacAssignedEvent"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 123 OF 134

Willdisplayeventsfromvm1whichwillberegardingcreationevents,andeventswhenwhen/whichmacaddresswasassigned.ParameterVMThisparameterisasinglestringrepresentingvmname.Itexpectssinglevmnamethatexistsinvirtualcenter.Atthismomentinearlyscriptversionitwillhandleonlyacasewherethereis1instanceofvmofselectedname.Infuture,itwillhandlemultipleaswell..ParametertypesIfnonespecifieditwillreturnallevents.Ifspecifiedwillreturnonlyeventswithselectedtypes.Forexample:"VmCreatedEvent","VmDeployedEvent","VmMacAssignedEvent""VmClonedEvent",etc....ParametercategoryPossiblecategoriesare:warning,info,error.Pleaseusethisparameterifyouwanttofilterevents..ParameterAllIfyouwillsetthisparameter,asaresultcommandwillqueryalleventsfromvirtualcenterserverregardingvirtualmachines..NotesNAME:VMEventsAUTHOR:GrzegorzKulikowskiLASTEDIT:11/09/2012NOTWORKING?#[email protected]://psvmware.wordpress.com#>param([Parameter(ValueFromPipeline=$true)][ValidatenotNullOrEmpty()]$VM,[String[]]$types,[string]$category,[switch]$All)$si=get-viewServiceInstance$em=get-view$si.Content.EventManager$EventFilterSpec=New-ObjectVMware.Vim.EventFilterSpec$EventFilterSpec.Type=$typesif($category){$EventFilterSpec.Category=$category}if($VM){$EventFilterSpec.Entity=New-ObjectVMware.Vim.EventFilterSpecByEntityswitch($VM){{$_-is[VMware.Vim.VirtualMachine]}{$VMmoref=$vm.moref}{$_-is[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]}{$VMmoref=$vm.Extensiondata.moref}default{$vmmoref=(get-view-ViewTypevirtualmachine-Filter@{'name'=$VM}).moref}}$EventFilterSpec.Entity.Entity=$vmmoref

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 124 OF 134

$em.QueryEvents($EventFilterSpec)}if($All){$em.QueryEvents($EventFilterSpec)}}functionget-vmcreationdate{<#.SynopsisGetswherepossiblevmcreationdate..DescriptionThisfunctionwillreturnobjectwithinformationaboutcreationtime,method,month,creatorforparticularvm.VMname:SomeVM12CreatedTime:8/10/201211:48:18AMCreatedMonth:AugustCreationMethod:ClonedCreator:office\gregThisfunctionwilldisplayNoEventvalueinpropertiesincasewhenyourVCdoesnolongerhaveinformationaboutthoseparticularevents,oryourvmeventsnolongerhaveentriesaboutbeingcreated.IfyourVCdatabasehaslongerretensiondateitismorepossiblethatyouwillfindthisevent..ExampleGet-VMCreationdate-VMnames"my_vm1","My_otherVM"Thiswillreturnobjectsthatcontaincreationdateinformationforvmswithnamesmyvm1andmyvm2.ExampleGet-VM-Location'Cluster1'|Get-VMCreationdateThiswillreturnobjectsthatcontaincreationdateinformationforvmsthatarelocatedinCluster1.ExampleGet-view-viewtypevirtualmachine-SearchRoot(get-datacenter'mydc').id|Get-VMCreationDateThiswillreturnobjectsthatcontaincreationdateinformationforvmsthatarelocatedindatacentercontainer'mydc'.Ifyouareusingthisfunctionwithinexistingloopwhereyouhavevmsfromget-viewcmdlet,youcanpassthemviapipeorasVMnamesparameter..Example$report=get-cluster'cl-01'|Get-VMCreationdate$report|export-csvc:\myreport.csvWillstoreallreportedcreationtimesobjectin$reportarrayvariableandexportreporttocsvfile.Youcanalsofilterthereportbeforewritingittocsvfileusingselect$report|Where-Object{$_.CreatedMonth-eq"October"}|SelectVMName,CreatedMonthSothatyouwillseeonlyvmsthatwerecreatedinOctober..Exampleget-vmcreationdate-VMnames"my_vm1",testvm55WARNING:my_vm1couldnotbefound,typo?VMname:testvm55CreatedTime:10/5/20122:24:03PMCreatedMonth:OctoberCreationMethod:NewVM

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 125 OF 134

Creator:home\gregInthecasewhenyourprovidedvmdoesnotexistsinyorinfrastructure,awarningwillbedisplayed.Youcanstillstorethewholereportin$reportvariable,butitwillnotincludeanyinformationaboutmissingvmcreationdates.Awarningwillbestilldisplayedonlyforyourinformationthattherewasprobablyatypointhevmname..ParameterVMnamesThisparametershouldcontainvirtualmachineobjectsorstringsthatrepresentsvmnames.ItispossibletofeedthisfunctionwiithVMobjectsthatcomefromget-vmorfromget-view..NotesNAME:Get-VMCreationdateAUTHOR:GrzegorzKulikowskiLASTEDIT:27/11/2012NOTWORKING?#[email protected]://psvmware.wordpress.com#>param([Parameter(ValueFromPipeline=$true,Mandatory=$true)][ValidateNotNullOrEmpty()][Object[]]$VMnames)process{foreach($vmin$VMnames){$ReportedVM=""|SelectVMname,CreatedTime,CreatedMonth,CreationMethod,Creatorif($CollectedEvent=$vm|Get-VMEvents-types'VmBeingDeployedEvent','VmRegisteredEvent','VmClonedEvent','VmBeingCreatedEvent'-ErrorActionSilentlyContinue){if($CollectedEvent.gettype().isArray){$CollectedEvent=$CollectedEvent|?{$_-is[vmware.vim.VmRegisteredEvent]}}$CollectedEventType=$CollectedEvent.gettype().name$CollectedEventMonth="{0:MMMM}"-f$CollectedEvent.CreatedTime$CollectedEventCreationDate=$CollectedEvent.CreatedTime$CollectedEventCreator=$CollectedEvent.Usernameswitch($CollectedEventType){'VmClonedEvent'{$CreationMethod='Cloned'}'VmRegisteredEvent'{$CreationMethod='RegisteredFromVMX'}'VmBeingDeployedEvent'{$CreationMethod='VmFromTemplate'}'VmBeingCreatedEvent'{$CreationMethod='NewVM'}default{$CreationMethod='Error'}}$ReportedVM.VMname=$CollectedEvent.vm.Name$ReportedVM.CreatedTime=$CollectedEventCreationDate$ReportedVM.CreatedMonth=$CollectedEventMonth$ReportedVM.CreationMethod=$CreationMethod$ReportedVM.Creator=$CollectedEventCreator}else{if($?){if($vm-is[VMware.Vim.VirtualMachine]){$ReportedVM.VMname=$vm.name}else{$ReportedVM.VMname=$vm.ToString()}$ReportedVM.CreatedTime='NoEvent'$ReportedVM.CreatedMonth='NoEvent'$ReportedVM.CreationMethod='NoEvent'$ReportedVM.Creator='NoEvent'}else{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 126 OF 134

$ReportedVM=$nullWrite-Warning"$VMcouldnotbefound,typo?"}}$ReportedVM}}}functionConvert-Size{[cmdletbinding()]param([validateset("Bytes","KB","MB","GB","TB")][string]$From,[validateset("Bytes","KB","MB","GB","TB")][string]$To,[Parameter(Mandatory=$true)][double]$Value,[int]$Precision=4)switch($From){"Bytes"{$value=$Value}"KB"{$value=$Value*1024}"MB"{$value=$Value*1024*1024}"GB"{$value=$Value*1024*1024*1024}"TB"{$value=$Value*1024*1024*1024*1024}}switch($To){"Bytes"{return$value}"KB"{$Value=$Value/1KB}"MB"{$Value=$Value/1MB}"GB"{$Value=$Value/1GB}"TB"{$Value=$Value/1TB}}return[Math]::Round($value,$Precision,[MidPointRounding]::AwayFromZero)}#################################################Settinglogdirectoryforengineandcurrentmonth################################################$CurrentMonth=get-date-formatyyyy.MM$CurrentLogDataDir=$OffsiteCloneLogDataDir+$CurrentMonth$CurrentTime=get-date-formathh.mm.ss#Testingpathexiststoenginelogging,ifnotcreatingit$ExportDataDirTestPath=test-path$CurrentLogDataDir$CurrentLogDataFile=$CurrentLogDataDir+"\$CurrentTime"+"-$VRAName"+"-OffsiteClone"+".txt"if($ExportDataDirTestPath-eq$False){New-Item-ItemTypeDirectory-Force-Path$CurrentLogDataDir}start-transcript-path$CurrentLogDataFile-NoClobber#################################################AddstheZertoPowershellCMDlets################################################functionLoadSnapin{param($PSSnapinName)if(!(Get-PSSnapin|where{$_.Name-eq$PSSnapinName})){Add-pssnapin-name$PSSnapinName}}LoadSnapin-PSSnapinName"Zerto.PS.Commands"#################################################ConnectingtovCenter-requiredforsuccessfulauthenticationwithZertoAPI################################################

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 127 OF 134

Try{connect-viserver-Server$vCenterServer-User$vCenterUser-Password$vCenterPassword}Catch[system.exception]{}Finally{}#################################################ForeachVPGtoClone################################################write-host"CloningupthefollowingVPGsonthisVRA:$VRAVPGsToClone"foreach($VPGin$VRAVPGsToClone){$VPGCloneStartTime=get-date$VPGName=$VPGwrite-host"CloningVPG:$VPGName"$VPGCloneDatastore=$OffsiteCloneVPGs|Where-Object{$_.VPGName-eq"$VPGName"}|Select-ObjectCloneDatstore-ExpandPropertyCloneDatastore$VPGUseLastVSS=$OffsiteCloneVPGs|Where-Object{$_.VPGName-eq"$VPGName"}|Select-ObjectUseLastVSS-ExpandPropertyUseLastVSS$VPGFailIfNoVSS=$OffsiteCloneVPGs|Where-Object{$_.VPGName-eq"$VPGName"}|Select-ObjectFailIfNoVSS-ExpandPropertyFailIfNoVSS$VMNames=$vmList|Where-Object{$_.VpgName-eq"$VPGName"}|selectVmName-ExpandPropertyVmName$VMCount=$VMNames.Count#################################################ForeachVPGtoClone#################################################GettingCPforVPGirrespectiveofVSSsettingTry{$cp_list=get-checkpoints-virtualprotectiongroup$VPGName-zvmip$ZVMIP-zvmport$ZVMPSPort-username$ZMVPSUser-password$ZVMPSPasswd-confirm:$false$last_cp=$cp_list[$cp_list.Count-1]#Changinglastcheckpointtimeintousablevalueforlogging$latesttimeobject=$last_cp|select-objecttimestamp|select-expandpropertytimestamp$VPGTimeSuffix=$latesttimeobject.ToString("yyy-MM-dd_HH-mm-ss")}Catch[system.exception]{}Finally{}#################################################IfuselastVSSCPisenabled################################################if($VPGUseLastVSS-eq"TRUE"){$lastvss_cpall=$cp_list|Where-Object{$_.Vss-eq$True}$lastvss_cp=$lastvss_cpall[$lastvss_cpall.Count-1]#Changinglastcheckpointtimeintousablevalueforlogging$latestvsstimeobject=$lastvss_cp|select-objecttimestamp|select-expandpropertytimestamp$VPGVSSTimeSuffix=$latesttimeobject.ToString("yyy-MM-dd_HH-mm-ss")}#################################################RunningoffsiteclonewherethereisnoVSScheckpointandjobissetnottofailifnoVSSfound################################################if(($lastvss_cp-eq$null)-and($VPGFailIfNoVSS-eq"False")){$VPGVSSCPUsed="FALSE"Try{clone-vpg-virtualprotectiongroup$VPGName-checkpoint$last_cp-datastore$VPGCloneDatastore-zvmip$ZVMIP-zvmport$ZVMPSPort-username$ZMVPSUser-password$ZVMPSPasswd-confirm:$false$OffsiteCloneJobSuccess="TRUE"}Catch[system.exception]

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 128 OF 134

{$OffsiteCloneJobSuccess="FALSE"}Finally{}#EndofoffsiteclonefornoVSSCPandjobsetnottofail}#################################################IfVSSCPisenabledandnotnull,runoffsitecloneagainsttheVSSCP################################################if($lastvss_cp-ne$null){$VPGVSSCPUsed="TRUE"#SettingVPGtimesuffixforVSSCP$VPGTimeSuffix=$VPGVSSTimeSuffix#RunningoffsiteclonewherethereisnoVSScheckpointandjobissetnottofailifnoVSSfoundTry{clone-vpg-virtualprotectiongroup$VPGName-checkpoint$lastvss_cp-datastore$VPGCloneDatastore-zvmip$ZVMIP-zvmport$ZVMPSPort-username$ZMVPSUser-password$ZVMPSPasswd-confirm:$false$OffsiteCloneJobSuccess="TRUE"}Catch[system.exception]{$OffsiteCloneJobSuccess="FALSE"}Finally{}#EndofoffsitecloneforVSSCP}#################################################NoVSSCPfoundandVPGFailIfNoVSSsettotrue################################################if(($lastvss_cp-ne$null)-and($VPGFailIfNoVSS-eq"True")){$VPGVSSCPUsed="FALSE"$OffsiteCloneJobSuccess="FALSE"write-host"NoVSSCPFoundandVPGFailIfNoVSSsettoTRUE"}#################################################GettingVMNamesfromVPGandCPtime################################################$VMNames=$vmList|Where-Object{$_.VpgName-eq"$VPGName"}|selectVmName-ExpandPropertyVmName$VMCount=$VMNames.Countwrite-host"CheckingforClonesofVMs:$VMNames"foreach($_in$VMNames){#SettingVMname$OffsiteCloneVMName=$_.VMName+"-"+$VPGTimeSuffix#################################################RunningVMexistchecktovalidateendofclonejoboutcome&time################################################$vmexistcounter=0$vmexistcountermax=720$vmexistsleep=120do{#$vmexistcounterremain=$vmexistcountermax-$vmexistcounterwrite-host"RunningVMExistCheckfor$OffsiteCloneVMNamein$vmexistsleepseconds,willtryanother$vmexistcounterremaintimes"sleep$vmexistsleep#GettingVMnameTry{$VMNameExists=Get-VM-name$OffsiteCloneVMName

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 129 OF 134

}Catch[system.exception]{}Finally{}if(!$VMNameExists){Write-Host"NoVMexistsnamed:$OffsiteCloneVMName"$VMExist="FALSE"}else{Write-Host"AVMexistsnamed:$OffsiteCloneVMName"$VMExist="TRUE"}$vmexistcounter++}until(($VMExist-eq"TRUE")-or($vmexistcounter-eq"480"))if($VMExist-eq"TRUE"){#CalculatingVMclonetimebasedonVMcreationdate$VMCloneEndTime=get-vm$OffsiteCloneVMName|Get-VMCreationDate|Select-ObjectCreatedTime-ExpandPropertyCreatedTime[datetime]$VMCloneEndTimeif($vCenterTimeZoneMatch-eq"FALSE"){#PShostandvCentertimezonesdon'tmatch,overwritingVMCLoneEndTimeotherwiseitwillshowthewrongtimetaken$VMCloneEndTime=get-date}$VMCloneTimeTakenTimeSpan=new-timespan-Start$VPGCloneStartTime-End$VMCloneEndTime$VMCloneTotalHours=$VMCloneTimeTakenTimeSpan|Select-ObjectTotalHours-ExpandPropertyTotalHours$VMCloneTotalDays=$VMCloneTimeTakenTimeSpan|Select-ObjectTotalDays-ExpandPropertyTotalDays#IfClonetookmorethan1day,addingdaystotimestring,ifnotthenitsremovedif($VMCloneTotalDays-ge1){$VMCloneTimeTaken=$VMCloneTimeTakenTimeSpan.ToString("dd\:hh\:mm\:ss")}else{$VMCloneTimeTaken=$VMCloneTimeTakenTimeSpan.ToString("hh\:mm\:ss")}write-host"VMCloneTimeTaken:$VMCloneTimeTaken"#GettingCloneVMsizeandcalcuatingthroughputinMb/sec$VMSizeinGB=(get-vm$OffsiteCloneVMName|Get-HardDisk|measure-ObjectCapacityGB-Sum).sum$VMSizeinTB=convert-size-FromGB-TOTB-Value$VMSizeinGB-Precision3$VMThroughputLong=($VMSizeinTB*1024*1024*8)/($VMCloneTotalHours*60*60)$VMThroughput=[Math]::Round($VMThroughputLong,2)}#RemovingVMfrominventoryifspecifiedinjobif($RemoveVMsFromInventory-eq"TRUE"){write-host"RemoveVMsFromInventorysettoTRUEExecuting:Remove-VM-VM$OffsiteCloneVMName-Confirm:$false"Try{Remove-VM-VM$OffsiteCloneVMName-Confirm:$false}Catch[system.exception]{#FailedtoremoveVM,waiting30secondsandtryingagainsleep30Try{Remove-VM-VM$OffsiteCloneVMName-Confirm:$false}Catch[system.exception]{

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 130 OF 134

}#Endoffirstcatch}Finally{}#EndoftrytoremoveVMfrominventory}#VMnotfoundininventory,failingjobif($VMExist-eq"FALSE"){$OffsiteCloneJobSuccess="FALSE"}#Resettingarray$OffsiteCloneVMLogArray=@()write-host"Adding$OffsiteCloneVMNametoOffsiteCloneVMLogArray"$OffsiteCloneVMLogArrayLine=new-objectPSObject$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCloneStart"-Value$VPGCloneStartTime$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCloneEnd"-Value$VMCloneEndTime$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCloneTimeTaken"-Value$VMCloneTimeTaken$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VpgName"-Value$VPGName$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"CheckpointTimeStamp"-Value$VPGTimeSuffix$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMName"-Value$OffsiteCloneVMName$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMSizeinTB"-Value$VMSizeinTB$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMSizeinGB"-Value$VMSizeinGB$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"ThroughputMbSec"-Value$VMThroughput$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMFoundInInventory"-Value$OffsiteCloneVMName$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMDatastore"-Value$VPGCloneDatastore$OffsiteCloneVMLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMDatastoreFolderName"-Value$OffsiteCloneVMName$OffsiteCloneVMLogArray+=$OffsiteCloneVMLogArrayLinewrite-host"VMArray:$OffsiteCloneVMLogArray"#ExportingtoCSV$OffsiteCloneVMLogCheck=test-path$OffsiteCloneVMLogif($OffsiteCloneVMLogCheck-eq$False){$OffsiteCloneVMLogArray|Sort-Object-PropertyVpgName|export-csv$OffsiteCloneVMLog-NoTypeInformation}else{$OffsiteCloneVMLogArray|Sort-Object-PropertyVpgName|export-csv$OffsiteCloneVMLog-Append-NoTypeInformation}#EndofforeachVMbelow}#EndofforeachVMabove##CalculatinglongestVMclonetimeandusingittocalculatelastthentotalVPGclonetime$VPGCloneEndTime=$OffsiteCloneVMLogArray|Sort-ObjectVMCloneEnd-Descending|select-objectVMCloneEnd-expandpropertyVMCloneEnd-First1$VPGCloneTimeTakenTimeSpan=new-timespan-Start$VPGCloneStartTime-End$VPGCloneEndTime$VPGCloneTotalDays=$VPGCloneTimeTakenTimeSpan|Select-ObjectTotalDays-ExpandPropertyTotalDays#IfClonetookmorethan1day,addingdaystotimestring,ifnotthenitsremovedif($VPGCloneTotalDays-ge1){$VPGCloneTimeTaken=$VPGCloneTimeTakenTimeSpan.ToString("dd\:hh\:mm\:ss")}else{$VPGCloneTimeTaken=$VPGCloneTimeTakenTimeSpan.ToString("hh\:mm\:ss")}#GettingtotalVMsizesofVPG$OffsiteCloneSizeinTB=($OffsiteCloneVMLogArray.VMSizeinTB|measure-sum|select-objectsum-expandpropertysum)$OffsiteCloneSizeinGB=($OffsiteCloneVMLogArray.VMSizeinGB|measure-sum|select-objectsum-expandpropertysum)$OffsiteCloneAverageThroughputMbSec=($OffsiteCloneVMLogArray.ThroughputMbSec|measure-average|select-objectaverage-expandpropertyaverage)#Creatingarray$OffsiteCloneVPGLogArray=@()#LoggingVPGJoboutcome

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 131 OF 134

$OffsiteCloneVPGLogArrayLine=new-objectPSObject$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"CloneStart"-Value$VPGTimeSuffix$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"CloneEnd"-Value$VPGCloneEndTime$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"CloneTimeTaken"-Value$VPGCloneTimeTaken$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VpgName"-Value$VPGName$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"JobSuccessful"-Value$OffsiteCloneJobSuccess$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"CheckpointTimeStamp"-Value$VPGTimeSuffix$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VPGFailIfNoVSS"-Value$VPGFailIfNoVSS$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VSSCheckpoint"-Value$VPGVSSCPUsed$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"TotalSizeinTB"-Value$OffsiteCloneSizeinTB$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"TotalSizeinGB"-Value$OffsiteCloneSizeinGB$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"AverageThroughputMbSec"-Value$OffsiteCloneAverageThroughputMbSec$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VMCount"-Value$VMCount$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"VRAName"-Value$VRAName$OffsiteCloneVPGLogArrayLine|Add-Member-MemberTypeNoteProperty-Name"CloneDatastore"-Value$VPGCloneDatastore$OffsiteCloneVPGLogArray+=$OffsiteCloneVPGLogArrayLine#Checkingtoseeiffileexists$OffsiteCloneVPGLogCheck=test-path$OffsiteCloneVPGLogif($OffsiteCloneVPGLogCheck-eq$False){$OffsiteCloneVPGLogArray|Sort-Object-PropertyVpgName|export-csv$OffsiteCloneVPGLog-NoTypeInformation}else{$OffsiteCloneVPGLogArray|Sort-Object-PropertyVpgName|export-csv$OffsiteCloneVPGLog-Append-NoTypeInformation}#CreatingperVPGemail$VPGHTMLMain=@"<html><head><metacontent="text/html;charset=ISO-8859-1"http-equiv="content-type"><title></title></head><body><tablestyle="text-align:left;width:100%;"border="1"cellpadding="2"cellspacing="2"><tbody><tr><tdstyle="vertical-align:top;">CloneStart</td><tdstyle="vertical-align:top;">CloneEnd</td><tdstyle="vertical-align:top;">CloneTimeTaken</td><tdstyle="vertical-align:top;">VpgName</td><tdstyle="vertical-align:top;">JobSuccessful</td><tdstyle="vertical-align:top;">CheckpointTimeStamp</td><tdstyle="vertical-align:top;">VSSCheckpointRequired</td><tdstyle="vertical-align:top;">VSSCheckpoint</td><tdstyle="vertical-align:top;">TotalSizeinTB</td><tdstyle="vertical-align:top;">TotalSizeinGB</td><tdstyle="vertical-align:top;">AverageThroughputMbSec</td><tdstyle="vertical-align:top;">VMCount</td><tdstyle="vertical-align:top;">VRAName</td><tdstyle="vertical-align:top;">CloneDatastore</td></tr>"@#CreatingHTMLRow$VPGHTMLRow="<tr><td>$VPGTimeSuffix</td><td>$VPGCloneEndTime</td><td>$VPGCloneTimeTaken</td><td>$VPGName</td><td>$OffsiteCloneJobSuccess</td><td>$VPGTimeSuffix</td><td>$VPGFailIfNoVSS</td><td>$VPGVSSCPUsed</td>

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 132 OF 134

<td>$OffsiteCloneSizeinTB</td><td>$OffsiteCloneSizeinGB</td><td>$OffsiteCloneAverageThroughputMbSec</td><td>$VMCount</td><td>$VRAName</td><td>$VPGCloneDatastore</td></tr>"$VPGHTMLTable+=$VPGHTMLRow#CompilingEndofHTMLemail$VPGHTMLEnd=@"</tbody></table><br></body></html>"@#CompilingFinalHTML$VPGHTML=$VPGHTMLMain+$VPGHTMLTable+$VPGHTMLEnd#SendingperVPGemail$EmailSubject="OffsiteCloneVPG:$VPGNameJobSuccess:$OffsiteCloneJobSuccess"#Nothingtoconfigurebelow#BuildingSMTPsettingsbeasedonsettings$emailsetting=New-ObjectSystem.Net.Mail.MailMessage$emailsetting.to.add($EmailTo)$emailsetting.from=$EmailFrom$emailsetting.IsBodyHTML="TRUE"$emailsetting.subject=$EmailSubject$emailsetting.body=$VPGHTML#CreatingSMTPobject$smtp=New-ObjectSystem.Net.Mail.SmtpClient($SMTPServer,$SMTPPort);#EnablingSSLifsetif($SMTPSSLEnabled-eq"TRUE"){$smtp.EnableSSL="TRUE"}#Settingcredentials$smtp.Credentials=New-ObjectSystem.Net.NetworkCredential($SMTPUser,$SMTPPassword);#SendingtheEmailTry{$smtp.send($emailsetting)}Catch[system.exception]{}Finally{}#ResettingHTMLemailrowtable$VPGHTMLTable=$null#EndofforeachVPGbelow}#EndofforeachVPGabove##################################################DisconnectingfromvCenter################################################disconnect-viserver$vCenterServer-Force-Confirm:$false#################################################Stoppinglogging################################################stop-transcript##Endofscriptblockbelow}#Endofscriptblockabove

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 133 OF 134

#################################################StartingJobtoexecutescriptblockforeachVRA#################################################SizesignificantlyreducedtostoplinebreakissueswhencopyingandpastingStart-Job$VRAScriptBlock-Name"$JobTime$VRANameOffsiteClone"-ArgumentList$VRAName,$VRAVPGsToClone,$vmList,$OffsiteCloneVPGs,$vCenterServer,$vCenterUser,$vCenterPassword,$ZVMIP,$ZVMPSPort,$ZMVPSUser,$ZVMPSPasswd,$OffsiteCloneVPGLog,$OffsiteCloneVMLog,$OffsiteCloneLogDataDir,$RemoveVMsFromInventory,$vCenterTimeZoneMatch,$EmailTo,$EmailFrom,$SMTPServer,$SMTPPort,$SMTPUser,$SMTPPassword,$SMTPSSLEnabled

#EndofforeachVRAbelow}#EndofforeachVRAabove

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER 134 OF 134

9 TROUBLESHOOTING One of the most powerful tools at your disposal when working with PowerShell and APIs it to encapsulate your Invoke-WebRequest commands inside a Try/Catch statement to get more information on the error being returned. This can be very useful for troubleshooting everything from complex VPG creation to initial authentication with the API. Following is an example of a try statement to aid with troubleshooting: Try{$xZertoSessionResponse=Invoke-WebRequest-Uri$xZertoSessionURL-Headers$headers-MethodPOST-Body$sessionBody-ContentType$TypeJSON}Catch{Write-Host$_.Exception.ToString()$error[0]|Format-List-Force}

A graphical editing tool such as PowerShell ISE is recommended for not only the first run of your powershell scripts, but to aid with realtime troubleshooting and should be used whenever an error is encountered:

By utilizing the transcipting method as described in section 2.5 it is possible to see any exceptions caught during the script runtime. This can be used in combination with the Zerto Virtual Manager logs to correlate the error if it was generated by the REST API. These logs can be found be browsing to the below file on the ZVM: c:\Program Files\Zerto\Zerto Virtual Replication\logs\logfile.csv