Automating ZVR with PowerShell and REST APIs Whitepaper - Zerto

0 downloads 133 Views 2MB Size Report
VMware PowerCLI, 5.5+, installed on the host running the script ...... These email reports can contain data from both th
VERSION 4.0 MAY 2018

Automating Zerto Virtual Replication with PowerShell & REST APIs Whitepaper

Table of Contents 1

2

3

4

5

6

INTRODUCTION .................................................................................................................................................... 4 1.1

Use Cases .....................................................................................................................................................................4

1.2 1.3

REST APIs .....................................................................................................................................................................4 Legal Disclaimer ...........................................................................................................................................................4

BASICS & BEST PRACTICES .................................................................................................................................... 5 2.1

Requirements ..............................................................................................................................................................5

2.2

Using Variables & Arrays .............................................................................................................................................5

2.3 2.4

Encrypting Passwords ..................................................................................................................................................6 Scripting Best Practices................................................................................................................................................6

2.5

Transcripts ...................................................................................................................................................................6

2.6

Loading Modules .........................................................................................................................................................7

2.7

Bypassing Certificate Warnings ...................................................................................................................................7

2.8

Establishing API Sessions .............................................................................................................................................8

2.9

Full Start of Script Example..........................................................................................................................................9

QUERYING & REPORTING ................................................................................................................................... 11 3.1

Use Cases .................................................................................................................................................................. 11

3.1

Listing Unprotected VMs .......................................................................................................................................... 11

3.2

Using Unprotected VM IDs ....................................................................................................................................... 11

3.3 3.4

Listing Protected VMs & VPGs .................................................................................................................................. 12 Long Term RPO & Storage Reporting to CSV ............................................................................................................ 13

3.5

Resource Reports...................................................................................................................................................... 15

3.6

Resource Report Use Cases ...................................................................................................................................... 17

3.7

VPG, VM, VDISK, VNIC & Re-IP Settings Report ....................................................................................................... 18

DAILY EMAIL REPORTS........................................................................................................................................ 27 4.1

Use Cases .................................................................................................................................................................. 27

4.2

Design Methodology ................................................................................................................................................ 27

4.3

Daily Email Report .................................................................................................................................................... 27

AUTOMATING DEPLOYMENT ............................................................................................................................. 67 5.1

Use Cases .................................................................................................................................................................. 67

5.2

Bulk Automated VRA Deployment ........................................................................................................................... 67

5.3

Bulk Automated VPG Creation – ZVM Only.............................................................................................................. 70

5.4

Bulk Automated VPG Creation – ZVM & ZCM .......................................................................................................... 76

5.5

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

AUTOMATING VM PROTECTION ........................................................................................................................ 89 6.1

Use Cases .................................................................................................................................................................. 89

6.2

Automating VM Protection by vSphere Folder - ZVM Only ..................................................................................... 89

6.3

Automating VM Protection by vSphere Folder - ZVM & ZCM .................................................................................. 97

6.4

Automating VM Protection with vRealize Orchestrator ........................................................................................ 104

6.5

Adding VMs to VPGs ............................................................................................................................................... 105

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

2 OF 134

7

8

9

BULK EDIT OPERATIONS ................................................................................................................................... 107 7.1

Bulk VPG Name Changing ....................................................................................................................................... 107

7.2

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

SCHEDULING OFFSITE CLONES ......................................................................................................................... 119 8.1

Use Cases ................................................................................................................................................................ 119

8.2

Design Methodology .............................................................................................................................................. 119

8.3

Scheduled Offsite Clone ......................................................................................................................................... 120

TROUBLESHOOTING ......................................................................................................................................... 134

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

3 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 &toTimeString=" + $EndDateTime + "&startIndex=0&count=500" $ResourceReport = Invoke-RestMethod -Uri $ResourceReportURL -TimeoutSec 100 -Headers $zertoSessionHeader -ContentType $TypeJSON $ResourceReportAPIArray = $ResourceReport.ArrayOfVmResourcesInfo.VmResourcesInfo # Building table from Array by VpgName

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

16 OF 134

$ResourceReportAPITable = $ResourceReport | Sort-Object -Property VpgName | format-table -Property Timestamp,VpgName,VmName,TargetVraName $ResourceReportAPITable

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: # Protected VMs by most RecoveryJournalUsedStorageInGB usage $ResourceReportAPITable = $ResourceReport | Sort-Object -Property RecoveryJournalUsedStorageInGB -Descending | format-table -Property Timestamp,VmName,VpgName, RecoveryJournalUsedStorageInGB,RecoveryVolumesUsedStorageInGB $ResourceReportAPITable # Protected VMs by most RecoveryVolumesStorageInGB usage $ResourceReportAPITable = $ResourceReport | Sort-Object -Property RecoveryVolumesUsedStorageInGB -Descending | format-table -Property Timestamp,VmName,VpgName,RecoveryVolumesProvisionedStorageInGB,RecoveryVolumesUsedStorageInGB $ResourceReportAPITable # Protected VMs by CPU and RAM size $ResourceReportAPITable = $ResourceReport | Sort-Object -Property NumberOfvCpu,MemoryInMB -Descending | format-table -Property Timestamp,VpgName,VmName,NumberOfvCpu,MemoryInMB,VmHardwareVersion $ResourceReportAPITable # Protected VMs by target host, then by CPU and RAM size, useful for balancing recovery $ResourceReportAPITable = $ResourceReport | Sort-Object -Property TargetHost,NumberOfvCpu,MemoryInMB -Descending | format-table -Property Timestamp,VpgName,VmName,TargetHost,NumberOfvCpu,MemoryInMB,VmHardwareVersion $ResourceReportAPITable # VMs replicating to a VRA with number of volumes per VM, useful for balancing replication $ResourceReportAPITable = $ResourceReport | Where-Object {$_.TargetVraName -eq "Z-VRA-192.168.0.14"} | format-table -Property Timestamp,VmName,VpgName,TargetVraName,NumberOfVolumes $ResourceReportAPITable # Protected VMs with a specific VM hardware version $ResourceReportAPITable = $ResourceReport | Where-Object {$_.VmHardwareVersion -eq "vmx-08"} | format-table -Property Timestamp,VmName,VpgName,VmHardwareVersion $ResourceReportAPITable # Protected VMs by highest average bandwidth usage (derived from time between samples) $ResourceReportAPITable = $ResourceReport | Sort-Object -Property BandwidthInBytes -Descending | format-table -Property Timestamp,VmName,VpgName,BandwidthInBytes $ResourceReportAPITable # Protected VMs to a specific > .tg {border-collapse:collapse;border-spacing:0;border-color:#aaa;} .tg td{font-family:Arial, sans-serif;font-size:10px;padding:10px 5px;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;} .tg th{font-family:Arial, sans-serif;font-size:10px;font-weight:bold;padding:10px 5px;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;}

"@ ################################################ # Creating CSV Save Function ################################################ Function Save-CSV{ Param($Array,$CSVFileName,$CSVDirectory) # Saving file to directory specified then returning file name to use for email $Timestamp = get-date $Now = $TimeStamp.ToString("yyyy-MM-dd HH-mm-ss ") $CSVName = $Now + $CSVFileName $CSVFile = $CSVDirectory + $CSVName + ".csv" $Array | Export-CSV -NoTypeInformation $CSVFile $CSVFile }

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

28 OF 134

################################################ # Creating Time function ################################################ Function Get-Time{ $Timestamp = get-date $Now = $TimeStamp.ToString("yyyy-MM-dd HH-mm-ss ") $Now } ################################################ # Creating Email Function ################################################ Function Email-ZVRReport{ Param($EmailTo,$Subject,$Body,$Attachment,$SMTPProfile) # Getting SMTP Profile Settings $EmailFrom = $SMTPProfile[0] $SMTPServer = $SMTPProfile[1] $SMTPPort = $SMTPProfile[2] $SMTPUser = $SMTPProfile[3] $SMTPPassword = $SMTPProfile[4] $SMTPSSLEnabled = $SMTPProfile[5] # Building SMTP settings based on settings $emailsetting = New-Object System.Net.Mail.MailMessage $emailsetting.to.add($EmailTo) $emailsetting.from = $EmailFrom $emailsetting.IsBodyHTML = "TRUE" $emailsetting.subject = $Subject $emailsetting.body = $Body # Adding attachments if ($Attachment -ne $null) { # Performing for each to support multiple attachments foreach ($_ in $Attachment) { $emailattachmentsetting = new-object System.Net.Mail.Attachment $_ $emailsetting.attachments.add($emailattachmentsetting) # invoke-expression $AttachmentCommand # End of for each attachment below } # End of for each attachment above } # Creating SMTP object $smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort); # Enabling SSL if set if ($SMTPSSLEnabled -eq "TRUE") { $smtp.EnableSSL = "TRUE" } # Setting credentials $smtp.Credentials = New-Object System.Net.NetworkCredential($SMTPUser, $SMTPPassword); # Sending the Email Try { $smtp.send($emailsetting) } Catch [system.exception] { # Trying email again $smtp.send($emailsetting) } # End of email function } ################################################ # Creating Report arrays ################################################ $ProtectedVPGArray = @() $ProtectedVMArray = @()

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

29 OF 134

$TargetVRAArray = @() $UnprotectedVMArray = @() $Target&toTimeString=" + $NowDateTime + "&startIndex=0&count=500" $ResourceReport = Invoke-RestMethod -Uri $ResourceReportURL -TimeoutSec 100 -Headers $TargetZVMSessionHeader -ContentType $TypeJSON ################################################ # Creating ProtectedVPGArray ################################################ # Getting VPGs $ProtectedVPGsURL = $SourceZVMBaseURL+"vpgs" $ProtectedVPGsCMD = Invoke-RestMethod -Uri $ProtectedVPGsURL -TimeoutSec 100 -Headers $SourceZVMSessionHeader_JSON -ContentType $TypeJSON foreach ($VPG in $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) # Converting priority $VPGPriority = $VMPriorityArray | Where-Object {$_.Number -eq $PriorityNumber} | select -ExpandProperty Name # Converting VM status $VPGStatus = $VMStatusArray | Where-Object {$_.Number -eq $StatusNumber} | select -ExpandProperty Name $VPGStatusDescription = $VMStatusArray | Where-Object {$_.Number -eq $StatusNumber} | select -ExpandProperty Description # Getting VPG Journal size $VPGResourceReport = $ResourceReport | Where-Object {$_.VpgName -eq $VPGName} # Calculating total Journal usage $VPGJournalUsage = $VPGResourceReport.RecoveryJournalUsedStorageInGB $VPGTotalJournalUsage = 0 foreach ($_ in $VPGJournalUsage) { $VPGTotalJournalUsage += $_

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

41 OF 134

} $VPGTotalJournalUsage = [math]::Round($VPGTotalJournalUsage,2) # Getting Alerts for the VPG for past 24 hours $Tomorrow = (get-date).AddDays(1) $Yesterday = (get-date).AddDays(-1) # Building URL $VPGAlertsURL = $SourceZVMBaseURL+"alerts?"+"startDate=$Yesterday&endDate=$Tomorrow&vpgIdentifier={$VPGIdentifier}&isDismissed=false" # Getting events $VPGAlertsCMD = Invoke-RestMethod -Uri $VPGAlertsURL -TimeoutSec 100 -Headers $SourceZVMSessionHeader_JSON -ContentType $TypeJSON $VPGLastAlert = $VPGAlertsCMD | select * -First 1 # Getting description of last alert $VPGLastAlertIdentifier = $VPGLastAlert.HelpIdentifier $VPGLastAlertDescription = $EventStatusArray | Where-Object {$_.Identifier -eq $VPGLastAlertIdentifier} | select -expandproperty Description # Calculating RPO violations in last 24 hours $VPGRPOAlerts = $VPGAlertsCMD | Where-Object {$_.HelpIdentifier -eq "VPG0009" -or $_.HelpIdentifier -eq "VPG0009"} | Measure-Object | select -ExpandProperty Count # Adding to array $ProtectedVPGArrayLine = new-object PSObject $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "SourcePOD" -Value $SourcePOD $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "TargetPOD" -Value $TargetPOD $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value $VPGName $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "VMCount" -Value $VMCount $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "Priority" -Value $VPGPriority $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "RPO" -Value $RPO $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "RPOAlerts" -Value $VPGRPOAlerts $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "Status" -Value $VPGStatus $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "SizeInGb" -Value $SizeInGb $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "JournalSizeInGb" -Value $VPGTotalJournalUsage $ProtectedVPGArrayLine | Add-Member -MemberType NoteProperty -Name "AlertDescription" -Value $VPGLastAlertDescription $ProtectedVPGArray += $ProtectedVPGArrayLine } # Getting VMs $ProtectedVMsURL = $SourceZVMBaseURL+"vms" $ProtectedVMsCMD = Invoke-RestMethod -Uri $ProtectedVMsURL -TimeoutSec 100 -Headers $SourceZVMSessionHeader_JSON -ContentType $TypeJSON # Adding to array $ProtectedVMs = $ProtectedVMsCMD | Sort-Object VpgName foreach ($VM in $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 # Converting priority $VMPriority = $VMPriorityArray | Where-Object {$_.Number -eq $PriorityNumber} | select -ExpandProperty Name # Converting VM status $VMStatus = $VMStatusArray | Where-Object {$_.Number -eq $StatusNumber} | select -ExpandProperty Name $VMStatusDescription = $VMStatusArray | Where-Object {$_.Number -eq $StatusNumber} | select -ExpandProperty Description # Gettong VM Journal size $VMResourceReport = $ResourceReport | Where-Object {$_.VmName -eq $VMName} | select -First 1 $VMSourceCluster = $VMResourceReport.SourceCluster $VMTargetCluster = $VMResourceReport.TargetCluster # Calculating total Journal usage $VMJournalUsage = $VMResourceReport.RecoveryJournalUsedStorageInGB $VMTotalJournalUsage = 0 foreach ($_ in $VMJournalUsage) { $VMTotalJournalUsage += $_ } $VMTotalJournalUsage = [math]::Round($VMTotalJournalUsage,2) # Creating array line $ProtectedVMArrayLine = new-object PSObject $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "SourcePOD" -Value "$SourcePOD" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "SourceCluster" -Value "$VMSourceCluster"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

42 OF 134

$ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "TargetPOD" -Value "$TargetPOD" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "TargetCluster" -Value "$VMTargetCluster" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value "$VPGName" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "VMName" -Value "$VMName" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "Priority" -Value "$VMPriority" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "RPO" -Value "$RPO" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "Status" -Value "$VMStatus" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "Disks" -Value "$VMDisks" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "SizeInGb" -Value "$SizeInGb" $ProtectedVMArrayLine | Add-Member -MemberType NoteProperty -Name "JournalSizeInGb" -Value "$VMTotalJournalUsage" $ProtectedVMArray += $ProtectedVMArrayLine } ################################################ # Creating TargetVRAArray ################################################ $TargetZVMHostsURL = $TargetZVMBaseURL+"virtualizationsites/"+$TargetLocalSiteIdentifier+"/hosts" $TargetZVMHostsCMD = Invoke-RestMethod -Uri $TargetZVMHostsURL -TimeoutSec 100 -Headers $TargetZVMSessionHeader -ContentType $TypeJSON $TargetZVMVRAsURL = $TargetZVMBaseURL+"vras" $TargetZVMVRAsCMD = Invoke-RestMethod -Uri $TargetZVMVRAsURL -TimeoutSec 100 -Headers $TargetZVMSessionHeader -ContentType $TypeJSON $TargetZVMVRAs = $TargetZVMVRAsCMD | Select-Object VraName,HostIdentifier,VraGroup,RecoveryCounters -Unique # For each VRA foreach ($TargetVRA in $TargetZVMVRAs) { $VRAName = $TargetVRA.VraName $VRACluster = get-vm $VRAName | Get-Cluster | select -expandproperty Name $VRAHostIdentifier = $TargetVRA.HostIdentifier $VRAVMs = $TargetVRA.RecoveryCounters.Vms $VRAHostIdentifier = $TargetVRA.HostIdentifier $VRAVolumes = $TargetVRA.RecoveryCounters.Volumes $VRAVpgs = $TargetVRA.RecoveryCounters.Vpgs $VRAGroup = $TargetVRA.VraGroup # Getting hostname $VRAHostname = $TargetZVMHostsCMD | Where-Object {$_.HostIdentifier -eq $VRAHostIdentifier} | select -ExpandProperty VirtualizationHostName # Getting over commit > $TableCaption SourcePOD TargetPOD VPGName VMCount Priority RPO RPOAlerts Status SizeInGB JournalSizeInGB AlertDescription "@ # Building HTML table $ProtectedVPGArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $SourcePOD = $_.SourcePOD $TargetPOD = $_.TargetPOD $VPGName = $_.VPGName $VMCount = $_.VMCount $Priority = $_.Priority $RPO = $_.RPO $RPOAlerts = $_.RPOAlerts $Status = $_.Status $SizeInGb = $_.SizeInGb $JournalSizeInGb = $_.JournalSizeInGb $AlertDescription = $_.AlertDescription # Building HTML table row $ProtectedVPGArrayHTMLTableRow = " $SourcePOD $TargetPOD $VPGName $VMCount $Priority $RPO $RPOAlerts $Status

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

53 OF 134

$SizeInGb $JournalSizeInGb $AlertDescription " # Adding rows to table $ProtectedVPGArrayHTMLTable += $ProtectedVPGArrayHTMLTableRow } # Compiling End of HTML email $ProtectedVPGArrayHTMLTableEnd = @"
"@ # Compiling Final HTML $ProtectedVPGArrayHTMLTable = $ProtectedVPGArrayHTMLTableStart + $ProtectedVPGArrayHTMLTable + $ProtectedVPGArrayHTMLTableEnd $ProtectedVPGArrayHTMLTable } ################################################ # Function for building HTML table for ProtectedVMArray ################################################ Function Create-ProtectedVMArrayTable { Param($Array,$TableCaption) $ProtectedVMArrayHTMLTableStart = @" $TableCaption "@ # Building HTML table $ProtectedVMArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $SourcePOD = $_.SourcePOD $SourceCluster = $_.SourceCluster $TargetPOD = $_.TargetPOD $TargetCluster = $_.TargetCluster $VPGName = $_.VPGName $VMName = $_.VMName $Priority = $_.Priority $Status = $_.Status $RPO = $_.RPO $Disks = $_.Disks $SizeInGb = $_.SizeInGb $JournalSizeInGb = $_.JournalSizeInGb # Building HTML table row $ProtectedVMArrayHTMLTableRow = "

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

54 OF 134

" # Adding rows to table $ProtectedVMArrayHTMLTable += $ProtectedVMArrayHTMLTableRow } # Compiling End of HTML email $ProtectedVMArrayHTMLTableEnd = @"
SourcePOD SourceCluster TargetPOD TargetCluster VPGName VMName Priority Status RPO Disks SizeInGB JournalSizeInGB
$SourcePOD $SourceCluster $TargetPOD $TargetCluster $VPGName $VMName $Priority $Status $RPO $Disks $SizeInGb $JournalSizeInGb

"@ # Compiling Final HTML $ProtectedVMArrayHTMLTable = $ProtectedVMArrayHTMLTableStart + $ProtectedVMArrayHTMLTable + $ProtectedVMArrayHTMLTableEnd $ProtectedVMArrayHTMLTable # End of ProtectedVMArrayTable function } ################################################ # Function for building HTML table for TargetVRAArray ################################################ Function Create-TargetVRAArrayTable { Param($Array,$TableCaption) $TargetVRAArrayHTMLTableStart = @" $TableCaption "@ # Building HTML table $TargetVRAArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $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 # Building HTML table row $TargetVRAArrayHTMLTableRow = "

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

55 OF 134

" # Adding rows to table $TargetVRAArrayHTMLTable += $TargetVRAArrayHTMLTableRow } # Compiling End of HTML email $TargetVRAArrayHTMLTableEnd = @"
TargetPOD VRACluster RecoveryVRAName ESXiHostname VRAVPGs VRAVMs VRAVolumes VRAVolumesTB VRAJournalsTB VMNumbervCPU VMCpuUsedGhz VMMemoryGB VMActiveMemoryGB
$TargetPOD $TargetCluster $VRAName $ESXiHostname $VRAVPGs $VRAVMs $VRAVolumes $VRARecoveryVolumesInTB $VRARecoveryJournalsInTB $VMNumberOfvCPU $VMCpuUsedInGhz $VMMemoryInGB $VMActiveMemoryInGB

"@ # Compiling Final HTML $TargetVRAArrayHTMLTable = $TargetVRAArrayHTMLTableStart + $TargetVRAArrayHTMLTable + $TargetVRAArrayHTMLTableEnd $TargetVRAArrayHTMLTable # End of TargetVRAArrayTable function } ################################################ # Function for building HTML table for UnprotectedVMArrayTable ################################################ Function Create-UnprotectedVMArrayTable { Param($Array,$TableCaption) $UnprotectedVMArrayHTMLTableStart = @" $TableCaption "@ # Building HTML table $UnprotectedVMArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $SourcePOD = $_.SourcePOD $VMFolder = $_.VMFolder $VMName = $_.VMName $VMCluster = $_.VMCluster $NumCPU = $_.NumCPU $MemoryGB = $_.MemoryGB $NICS = $_.NICS $HardDisks = $_.HardDisks $UsedSpaceGB = $_.UsedSpaceGB # Building HTML table row $UnprotectedVMArrayHTMLTableRow = "

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

56 OF 134

" # Adding rows to table $UnprotectedVMArrayHTMLTable += $UnprotectedVMArrayHTMLTableRow } # Compiling End of HTML email $UnprotectedVMArrayHTMLTableEnd = @"
SourcePOD VMFolder VMName VMCluster NumCPU MemoryGB NICS HardDisks UsedSpaceGB
$SourcePOD $VMFolder $VMName $VMCluster $NumCPU $MemoryGB $NICS $HardDisks $UsedSpaceGB

"@ # Compiling Final HTML $UnprotectedVMArrayHTMLTable = $UnprotectedVMArrayHTMLTableStart + $UnprotectedVMArrayHTMLTable + $UnprotectedVMArrayHTMLTableEnd $UnprotectedVMArrayHTMLTable # End of TargetVRAArrayTable function } ################################################ # Function for building HTML table for Target> $TableCaption PODName >>UsedByZVR CapacityGB FreeSpaceGB FreePercent "@ # Building HTML table $Targettg-yw4l"">$PODName $tg-yw4l"">$tg-yw4l"">$UsedByZVR $CapacityGB $FreeSpaceGB $FreePercent " # Adding rows to table

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

57 OF 134

$Target> $TableCaption PODName VPGName VMCount Priortiy ProtectedSiteName RecoverySiteName RpoInSeconds TestIntervalInMinutes UseWanCompression BootGroupCount BootGroupNames BootGroupDelays JournalHistoryInHours Journal>Journal>JournalHardLimitInMB JournalHardLimitInPercent JournalWarningThresholdInMB JournalWarningThresholdInPercent FailoverNetworkName FailoverTestNetworkName Default>DefaultFolderName DefaultHostClusterName DefaultHostName "@ # Building HTML table $VPGArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $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

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

58 OF 134

$VPGUseWanCompression = $_.VPGUseWanCompression $VPGZorgIdentifier = $_.VPGZorgIdentifier $VPGBootGroupCount = $_.VPGBootGroupCount $VPGBootGroupNames = $_.VPGBootGroupNames $VPGBootGroupDelays = $_.VPGBootGroupDelays $VPGBootGroupIdentifiers = $_.VPGBootGroupIdentifiers $VPGJournalHistoryInHours = $_.VPGJournalHistoryInHours $VPGJournaltg-yw4l"">$PODName $VPGName $VPGVMCount $VPGPriortiy $VPGProtectedSiteName $VPGRecoverySiteName $VPGRpoInSeconds $VPGTestIntervalInMinutes $VPGUseWanCompression $VPGBootGroupCount $VPGBootGroupNames $VPGBootGroupDelays $VPGJournalHistoryInHours $VPGJournaltg-yw4l"">$VPGJournaltg-yw4l"">$VPGJournalHardLimitInMB $VPGJournalHardLimitInPercent $VPGJournalWarningThresholdInMB $VPGJournalWarningThresholdInPercent $VPGFailoverNetworkName $VPGFailoverTestNetworkName $VPGDefaulttg-yw4l"">$VPGDefaultFolderName $VPGDefaultHostClusterName $VPGDefaultHostName " # Adding rows to table $VPGArrayHTMLTable += $VPGArrayHTMLTableRow } # Compiling End of HTML email $VPGArrayHTMLTableEnd = @"

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

59 OF 134


"@ # Compiling Final HTML $VPGArrayHTMLTable = $VPGArrayHTMLTableStart + $VPGArrayHTMLTable + $VPGArrayHTMLTableEnd $VPGArrayHTMLTable # End of TargetVRAArrayTable function } ################################################ # Function for building HTML table for VMArray ################################################ Function Create-VMArrayTable { Param($Array,$TableCaption) $VMArrayHTMLTableStart = @" $TableCaption "@ # Building HTML table $VMArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $PODName = $_.SourcePOD $VPGName = $_.VPGName $VPGidentifier = $_.VPGidentifier $VMName = $_.VMName $VMIdentifier = $_.VMIdentifier $VMNICCount = $_.VMNICCount $VMVolumeCount = $_.VMVolumeCount $VMProvisionedStorageInMB = $_.VMProvisionedStorageInMB $VMUsedStorageInMB = $_.VMUsedStorageInMB $VMBootGroupName = $_.VMBootGroupName $VMBootGroupDelay = $_.VMBootGroupDelay $VMBootGroupIdentifier = $_.VMBootGroupIdentifier $VMJournaltg-yw4l"">$PODName " # Adding rows to table $VMArrayHTMLTable += $VMArrayHTMLTableRow } # Compiling End of HTML email $VMArrayHTMLTableEnd = @"
PODName VPGName VMName NICCount VolumeCount ProvisionedStorageInMB UsedStorageInMB BootGroupName BootGroupDelay Journal>Journal>JournalHardLimitInMB JournalHardLimitInPercent >>FolderName HostClusterName HostName
$VPGName $VMName $VMNICCount $VMVolumeCount $VMProvisionedStorageInMB $VMUsedStorageInMB $VMBootGroupName $VMBootGroupDelay $VMJournaltg-yw4l"">$VMJournaltg-yw4l"">$VMJournalHardLimitInMB $VMJournalHardLimitInPercent $VMtg-yw4l"">$VMtg-yw4l"">$VMFolderName $VMHostClusterName $VMHostName

"@ # Compiling Final HTML $VMArrayHTMLTable = $VMArrayHTMLTableStart + $VMArrayHTMLTable + $VMArrayHTMLTableEnd $VMArrayHTMLTable # End of TargetVRAArrayTable function } ################################################ # Function for building HTML table for VMVolumeArray ################################################ Function Create-VMVolumeArrayTable { Param($Array,$TableCaption) $VMVolumeArrayHTMLTableStart = @" $TableCaption

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

62 OF 134

"@ # Building HTML table $VMNICArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $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 # Building HTML table row $VMNICArrayHTMLTableRow = " " # Adding rows to table $VMNICArrayHTMLTable += $VMNICArrayHTMLTableRow }

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

63 OF 134

# Compiling End of HTML email $VMNICArrayHTMLTableEnd = @"
PODName VPGName VMName VolumeID VolumeIsSWAP VolumeIsThin Volume>Volumetg-yw4l"">$PODName $VPGName $VMName $VMVolumeID $VMVolumeIsSWAP $VMVolumeIsThin $VMVolumetg-yw4l"">$VMVolume> $TableCaption
PODName VPGName VMName VMNICIdentifier FailoverNetworkName FailoverDNSSuffix FailoverShouldReplaceMacAddress FailoverGateway FailoverDHCP FailoverPrimaryDns FailoverSecondaryDns FailoverStaticIp FailoverSubnetMask FailoverTestNetworkName FailoverTestDNSSuffix FailoverTestShouldReplaceMacAddress FailoverTestGateway FailoverTestDHCP FailoverTestPrimaryDns FailoverTestSecondaryDns FailoverTestStaticIp FailoverTestSubnetMask
$PODName $VPGName $VMName $VMNICIdentifier $VMNICFailoverNetworkName $VMNICFailoverDNSSuffix $VMNICFailoverShouldReplaceMacAddress $VMNICFailoverGateway $VMNICFailoverDHCP $VMNICFailoverPrimaryDns $VMNICFailoverSecondaryDns $VMNICFailoverStaticIp $VMNICFailoverSubnetMask $VMNICFailoverTestNetworkName $VMNICFailoverTestDNSSuffix $VMNICFailoverTestShouldReplaceMacAddress $VMNICFailoverTestGateway $VMNICFailoverTestDHCP $VMNICFailoverTestPrimaryDns $VMNICFailoverTestSecondaryDns $VMNICFailoverTestStaticIp $VMNICFailoverTestSubnetMask

"@ # Compiling Final HTML $VMNICArrayHTMLTable = $VMNICArrayHTMLTableStart + $VMNICArrayHTMLTable + $VMNICArrayHTMLTableEnd $VMNICArrayHTMLTable # End of TargetVRAArrayTable function } ################################################ # Function for building HTML table for PODSummaryArray ################################################ Function Create-PODSummaryArrayTable { Param($Array,$TableCaption) $PODSummaryArrayHTMLTableStart = @" $TableCaption "@ # Building HTML table $PODSummaryArrayHTMLTable = $null foreach ($_ in $Array) { # Setting values $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 # Building HTML table row $PODSummaryArrayHTMLTableRow = "

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

64 OF 134

" # Adding rows to table $PODSummaryArrayHTMLTable += $PODSummaryArrayHTMLTableRow } # Compiling End of HTML email $PODSummaryArrayHTMLTableEnd = @"
PODName VMs Protected UnProtected VPGs MeetingSLA NotMeetingSLA AverageRPO HighPriority MediumPriority LowPriority ProtectedSizeTB JournalSizeTB
$PODName $VMs $VMsProtected $VMsUnProtected $VPGs $MeetingSLA $NotMeetingSLA $AverageRPO $HighPriority $MediumPriority $LowPriority $ProtectedSizeTB $JournalSizeTB

"@ # Compiling Final HTML $PODSummaryArrayHTMLTable = $PODSummaryArrayHTMLTableStart + $PODSummaryArrayHTMLTable + $PODSummaryArrayHTMLTableEnd $PODSummaryArrayHTMLTable # End of TargetVRAArrayTable function } ######################################################################################################################## # Customize reports below ######################################################################################################################## ######################################################################### # Building & Sending Report - POD Summary Report ######################################################################### # Setting Email subject $Subject = "Zerto POD Summary Report" # Creating Tables for Email Body # Table1 $PODSummaryArraySorted = $PODSummaryArray | Sort-Object PODName $PODSummaryArrayHTML = Create-PODSummaryArrayTable -Array $PODSummaryArraySorted -TableCaption "POD Summary" # Table2 $VPGAlerts = $ProtectedVPGArray | Where-Object {$_.Status -ne "MeetingSLA" -or $_.RPOAlerts -ge "1"} | Sort-Object SourcePOD,VPGName if ($VPGAlertArraySorted -ne $null) { $VPGAlertArrayHTML = Create-ProtectedVPGArrayTable -Array $VPGAlerts -TableCaption "All VPG Violations by POD and VPGName" } else { $VPGAlertArrayHTML = $null } # Table2 $Target&toTimeString=" + $NowDateTime + "&startIndex=0&count=500"

$ResourceReport = Invoke-RestMethod -Uri $ResourceReportURL -TimeoutSec 100 -Headers $zertoSessionHeader -ContentType $TypeJSON # Importing CSV for VPGs $OffsiteCloneVPGs = import-csv $OffsiteCloneVPGsCSV $OffsiteCloneVPGsByName = $OffsiteCloneVPGs | Select-Object VPGName -ExpandProperty VPGName # Building list of VRAs that have VPGs to clone $TargetSiteVPGs = $ResourceReport | Sort-Object VPGName -Unique | Select VPGName,TargetVraName ################################################ # Building array of VRAs that have VPGs that require offsite clone ################################################ $TargetVRAArray = @() foreach ($VPG in $TargetSiteVPGs) { if ($OffsiteCloneVPGsByName -ccontains $VPG.VpgName) { $TargetVRAArrayLine = new-object PSObject $TargetVRAArrayLine | Add-Member -MemberType NoteProperty -Name "VPGName" -Value $VPG.VpgName $TargetVRAArrayLine | Add-Member -MemberType NoteProperty -Name "VRAName" -Value $VPG.TargetVraName $TargetVRAArray += $TargetVRAArrayLine

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

121 OF 134

} } $TargetSiteVRAs = $TargetVRAArray | Select-Object VRAName -Unique ################################################################################################ # Performing for each VRA that has an offsite clone to run action ################################################################################################ foreach ($VRA in $TargetSiteVRAs) { $VRAName = $VRA.VRAName # Getting the VPGs to clone for the current VPG $VRAVPGsToClone = $null $VRAVPGsToClone = $TargetVRAArray | where-object {$_.VRAName -eq "$VRAName"} | Select-Object VPGName -ExpandProperty VPGName -Unique write-host "$VRAName VPGs to clone: $VRAVPGsToClone" $Now = get-date $JobTime = $Now.ToString("yyy-MM-dd_HH-mm-ss") ################################################ # Script block within per VRA ################################################ $VRAScriptBlock = { param ( $VRAName, $VRAVPGs ToClone, $vmLis t, $OffsiteCloneVPGs, $vCenterServer, $vCenterUser, $vCenterPassword, $ZVMIP, $ZVMPSPort, $ZMVPSUser, $ZVMPSPasswd, $OffsiteCloneVPGLog, $OffsiteCl oneVMLog, $Offsi teCloneLog http-equiv="content-type"> "@ # Creating HTML Row $VPGHTMLRow = "

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

131 OF 134

" $VPGHTMLTable += $VPGHTMLRow # Compiling End of HTML email $VPGHTMLEnd = @"
CloneStart CloneEnd CloneTimeTaken VpgName JobSuccessful CheckpointTimeStamp VSSCheckpointRequired VSSCheckpoint TotalSizeinTB TotalSizeinGB AverageThroughputMbSec VMCount VRAName CloneDatastore
$VPGTimeSuffix $VPGCloneEndTime $VPGCloneTimeTaken $VPGName $OffsiteCloneJobSuccess $VPGTimeSuffix $VPGFailIfNoVSS $VPGVSSCPUsed $OffsiteCloneSizeinTB $OffsiteCloneSizeinGB $OffsiteCloneAverageThroughputMbSec $VMCount $VRAName $VPGCloneDatastore

"@ # Compiling Final HTML $VPGHTML = $VPGHTMLMain + $VPGHTMLTable + $VPGHTMLEnd # Sending per VPG email $EmailSubject = "OffsiteCloneVPG:$VPGName JobSuccess:$OffsiteCloneJobSuccess" # Nothing to configure below # Building SMTP settings beased on settings $emailsetting = New-Object System.Net.Mail.MailMessage $emailsetting.to.add($EmailTo) $emailsetting.from = $EmailFrom $emailsetting.IsBodyHTML = "TRUE" $emailsetting.subject = $EmailSubject $emailsetting.body = $VPGHTML # Creating SMTP object $smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort); # Enabling SSL if set if ($SMTPSSLEnabled -eq "TRUE") { $smtp.EnableSSL = "TRUE" } # Setting credentials $smtp.Credentials = New-Object System.Net.NetworkCredential($SMTPUser, $SMTPPassword); # Sending the Email Try { $smtp.send($emailsetting) } Catch [system.exception] { } Finally { } # Resetting HTML email row table $VPGHTMLTable = $null # End of for each VPG below } # End of for each VPG above # ################################################ # Disconnecting from vCenter ################################################ disconnect-viserver $vCenterServer -Force -Confirm:$false ################################################ # Stopping logging ################################################ stop-transcript # # End of script block below } # End of script block above

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

132 OF 134

################################################ # Starting Job to execute script block for each VRA ################################################ # Size significantly reduced to stop line break issues when copying and pasting Start-Job $VRAScriptBlock -Name "$JobTime $VRAName OffsiteClone" -ArgumentList $VRAName, $VRAVPGsToClone, $vmList, $OffsiteCloneVPGs, $vCenterServer, $vCenterUser, $vCenterPassword, $ZVMIP, $ZVMPSPort, $ZMVPSUser, $ZVMPSPasswd, $OffsiteCloneVPGLog, $OffsiteCloneVMLog, $OffsiteClone Log DataDir, $RemoveVMsFromInventory, $vCenterTimeZoneMatch, $EmailTo, $EmailFrom, $SMTPServer, $SMTPPort, $SMTPUser, $SMTPPassword, $SMTPSSLEnabled

# End of for each VRA below } # End of for each VRA above

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

133 OF 134

9

TROUBLESHOOTING

One of the most powerful tools at your disposal when working with PowerShell and APIs is 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 -Method POST -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 real-time 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

AUTOMATING ZVR WITH POWERSHELL & REST API WHITEPAPER

134 OF 134