This post will give you information about step to RP4VM consistency group creation API. I created on PowerCLI script to create consistency group with in build Power Shell command Invoke-Restmethod.
This script assumes you have two different vCenters. One for Production and one for Disaster recovery. Feel free to customize it based on your requirement.
- Source Site is in different VC and
- Target site is in different VC.
Please refer below script.
Note – You need to create RP4VM Cluster on both sides, need to join them before running this script. I wrote OVA Deployment script and can be found here RP4VM OVA Deployment via PowerCLI
$global:DateTime = $((Get-Date).ToString('yyyy-MM-dd-hh-mm')) $logpath = "$HOME\Desktop\CGCreation-log-$global:DateTime.txt" Start-Transcript -path $logpath -append & 'C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1' Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -DefaultVIServerMode Multiple $cred = Get-Credential $RPCred = Get-Credential Connect-VIServer Sourcevcenter1, Targetvcenter2 -Credential $cred ##### Collect vCenter Instance UID ################ $vc = $global:DefaultVIServers | sort Name $sourcevc = $vc[0].InstanceUuid $targetvc = $vc[1].InstanceUuid ############### Change setting to ignore TLS/SSL Error ################################ if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type) { $certCallback = @" using System; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; public class ServerCertificateValidationCallback { public static void Ignore() { if(ServicePointManager.ServerCertificateValidationCallback ==null) { ServicePointManager.ServerCertificateValidationCallback += delegate ( Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors ) { return true; }; } } } "@ Add-Type $certCallback } [ServerCertificateValidationCallback]::Ignore() $dc = 'dc name' foreach ($storenumber in $dc){ ######### Get source vRPACluster IP ###################################### $ucscluster = Get-Cluster $storenumber -Server $vc[0] | Get-VM | where {$_.Name -like "*vRPA1"} |Select Name, @{N="IPAddress";E={@($_.guest.IPAddress[0])}} $split = $ucscluster.IPAddress $IPbyte = $split.Split(".") $newucsip = ($IPByte[0]+"."+$IPByte[1]+"."+$IPByte[2]) $startip = "102" $clusipaddress = "$newucsip.$($startip+$_)" ############# Get vRPA Cluster Details ########################### $uri = "https://$clusipaddress/fapi/rest/5_2/settings" $jsonnew = Invoke-RestMethod -Uri $uri -Method Get -credential $RPCred -ContentType application/json ############## RPA Cluster Name #################### $ucsRPAClustername = $jsonnew.systemSettings.clustersSettings.clusterName[1] $targetvRPAClustername = $jsonnew.systemSettings.clustersSettings.clusterName[0] ############### source and Target RPA Cluster ID ################ $ucsRPAClusterID = $jsonnew.systemSettings.clustersSettings.clusterUID[1].id $targetRPAClusterID = $jsonnew.systemSettings.clustersSettings.ClusterUID[0].id ############### Get Array ID ######################## $sourcearrayUID = $jsonnew.systemSettings.clustersSettings.ampsSettings.managedArrays[1].arrayUID.id $targetarrayUID = $jsonnew.systemSettings.clustersSettings.ampsSettings.managedArrays[0].arrayUID.id ##################Storage ResourcePoolID ################ $storageDSUID = $jsonnew.systemSettings.clustersSettings.ampsSettings.managedArrays[1].resourcePools.resourcePoolUID | sort StorageResourcePoolId $sourceFirstDSID = $storageDSUID.StorageResourcePoolID[0] $sourceSecondDSUID = $storageDSUID.StorageResourcePoolID[1] $DSUid = @() $DSUid += $sourceFirstDSID $DSUid += $sourceSecondDSUID $DS = 0 $targetDSUID = $jsonnew.systemSettings.clustersSettings.ampsSettings.managedArrays[0].resourcePools.resourcePoolUID.storageResourcePoolId #$storage = (Get-Cluster $storenumber -Server $vc[1] | Get-VMHost | select -First 1 | Get-Datastore | where {$_.Name -like "VxRail*"}).ExtensionData.info.containerid ################### Pool ID ############### $UCSPoolID = $jsonnew.systemSettings.clustersSettings.ampsSettings.managedArrays[1].resourcePools.resourcePoolUID | sort StorageResourcePoolId $UCSPoolIDFirst = $UCSPoolID.uuid[0] $UCSPoolIDSecond = $UCSPoolID.uuid[1] $targetPoolID = $jsonnew.systemSettings.clustersSettings.ampsSettings.managedArrays[0].resourcePools.resourcePoolUID.uuid[0] ############### Target ESXi UID ##################### $RestURL = Invoke-RestMethod https://$clusipaddress/fapi/rest/5_2/clusters/$targetRPAClusterID/virtual_infra_configuration -Method Get -credential $RPCred $targetESXiClusterID = $RestURL.virtualCentersConfiguration.datacentersConfiguration.esxClustersConfiguration.esxsConfiguration | sort Name $firstESX = $targetESXiClusterID.esxUID[0].uuid #$box = (Get-Cluster '04' -Server $vc[1] | Get-VMHost | select -First 1).extensiondata.hardware.systeminfo.uuid $secondESX = $targetESXiClusterID.esxUID[1].uuid $thirdESX = $targetESXiClusterID.esxUID[2].uuid $sample = @() $sample += $firstESX $sample += $secondESX $sample += $thirdESX $targetDatacenterUID = $RestURL.virtualCentersConfiguration.datacentersConfiguration.datacenterUID.uuid $sourcecluster = Get-Cluster $storenumber -Server $vc[0] $i = 0 foreach ($vm in $sourcecluster | Get-VM | where {$_.powerstate -ne "poweredoff" -and $_.Name -notlike "*Vs*" -and $_.Name -notlike "Prod*"-and $_.Name -notlike "OST-$($sourcecluster)-W16C"} | sort name){ if ($vm.Name -like "*Windows2016*"){ $cgname = $sourcecluster.Name + "_" + $vm.Name + "_CG" } else{ $cgname = $vm.Name + "_CG"} #$datastore = $DSUid[$DS] ########## Collect VM UID ###################### $vmuid = ($vm).extensiondata.config.instanceUuid $vmuid #### Calculate 30% of Total VM size ########################## $hdd = $vm | Get-HardDisk | Measure-Object -Sum CapacityGB $sourceHDD = [math]::Round($hdd.Sum * (20/100)) $targetHDD = [math]::Round($hdd.Sum * (30/100)) ##### Journal size in bytes ############# $sourcejournalsize = [math]::Round($sourceHDD*1024*1024*1024) $targetjournalsize = [math]::Round($targetHDD*1024*1024*1024) $box = $sample[$i] $datastoreid = $DSUid[$DS] $json = @" { "cgName" : "$cgname", "productionCopy" : { "clusterUID":{ "id" : $ucsRPAClusterID }, "copyUID": 0 }, "vmReplicationSets": [{ "replicationSetVms": [{ "copyUID": { "clusterUID": { "id": $targetRPAClusterID }, "copyUID": 1 }, "vmParam": { "JsonSubType": "CreateVMParam", "targetVirtualCenterUID": { "uuid": "$targetvc" }, "targetResourcePlacementParam": { "JsonSubType": "CreateTargetVMManualResourcePlacementParam", "targetEsxUID": { "uuid": "$box" } }, "targetDatastoreUID": { "uuid": "$targetDSUID" } } }, { "copyUID": { "clusterUID": { "id": $ucsRPAClusterID }, "copyUID": 0 }, "vmParam": { "JsonSubType": "ExistingVMParam", "vmUID": { "uuid": "$vmuid", "virtualCenterUID": { "uuid": "$sourcevc" } } } } ], "virtualHardwareReplicationPolicy": { "provisionPolicy": "SAME_AS_SOURCE", "hwChangesPolicy": "REPLICATE_HW_CHANGES" }, "virtualDisksReplicationPolicy": { "autoReplicateNewVirtualDisks": "true" } } ], "links": [{ "linkPolicy": { "JsonSubType": "ConsistencyGroupLinkPolicy", "protectionPolicy": { "protectionType": "ASYNCHRONOUS", "syncReplicationLatencyThresholds": { "resumeSyncReplicationBelow": { "value": 3000, "type": "MICROSECONDS" }, "startAsyncReplicationAbove": { "value": 5000, "type": "MICROSECONDS" }, "thresholdEnabled": "true" }, "syncReplicationThroughputThresholds": { "resumeSyncReplicationBelow": { "value": 35000, "type": "KB" }, "startAsyncReplicationAbove": { "value": 45000, "type": "KB" }, "thresholdEnabled": "false" }, "rpoPolicy": { "allowRegulation": "false", "maximumAllowedLag": { "value": 25, "type": "SECONDS" }, "minimizationType": "IRRELEVANT" }, "replicatingOverWAN": "true", "compression": "LOW", "bandwidthLimit": "0.0", "measureLagToTargetRPA": "true", "deduplication": "true", "weight": 1 }, "advancedPolicy": { "snapshotGranularity": "DYNAMIC", "performLongInitialization": "true" }, "snapshotShippingPolicy": null }, "linkUID": { "groupUID": { "id": 0 }, "firstCopy": { "clusterUID": { "id": $ucsRPAClusterID }, "copyUID": 0 }, "secondCopy": { "clusterUID": { "id": $targetRPAClusterID }, "copyUID": 1 } } } ], "copies": [{ "copyUID": { "clusterUID": { "id": $targetRPAClusterID }, "copyUID": 1 }, "copyName": "copy1", "JsonSubType": "ConsistencyGroupCopyParam", "volumeCreationParams": { "volumeParams": [{ "JsonSubType": "VolumeCreationParams", "volumeSize": { "sizeInBytes": $targetjournalsize }, "arrayUid": { "id": $targetarrayUID, "clusterUID": { "id": $targetRPAClusterID } }, "poolUid": { "uuid": $targetPoolID, "storageResourcePoolId": "$targetDSUID", "arrayUid": { "id": $targetarrayUID, "clusterUID": { "id": $targetRPAClusterID } } }, "tieringPolicy": null, "resourcePoolType": "VC_DATASTORE" } ] } }, { "copyUID": { "clusterUID": { "id": $ucsRPAClusterID }, "copyUID": 0 }, "copyName": "$vm", "JsonSubType": "ConsistencyGroupCopyParam", "volumeCreationParams": { "volumeParams": [{ "JsonSubType": "VolumeCreationParams", "volumeSize": { "sizeInBytes": $sourcejournalsize }, "arrayUid": { "id": $sourcearrayUID, "clusterUID": { "id": $ucsRPAClusterID } }, "poolUid": { "uuid": $UCSPoolIDFirst, "storageResourcePoolId": "$datastoreid", "arrayUid": { "id": $sourcearrayUID, "clusterUID": { "id": $ucsRPAClusterID } } }, "tieringPolicy": null, "resourcePoolType": "VC_DATASTORE" } ] } } ], "startTransfer" : false, "JsonSubType": "ReplicateVmsParam" } "@ if($i -eq 2){ $i = 0 } else{ $i ++ } if($DS -eq 1){ $DS = 0 } else { $DS ++ } #$body = $json | ConvertTo-Json; #$test = $body | ConvertTo-Json; $REST= "https://$clusipaddress/fapi/rest/5_2/groups/virtual_machines/replicate" try{ $response = Invoke-RestMethod -Method Post -Uri $REST -Body $json -ContentType application/json -credential $RPCred -OutFile $HOME\Desktop\test_protection.txt } Catch [System.Net.WebException] { $exception = $_.Exception $respstream = $exception.Response.GetResponseStream() $sr = new-object System.IO.StreamReader $respstream $ErrorResult = $sr.ReadToEnd() write-host $ErrorResult } catch { # Dig into the exception to get the Response details. # Note that value__ is not a typo. Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription } sleep -Seconds 180 } ############## Start CG replication ######################### $state = "https://${clusipaddress}/fapi/rest/5_2/groups/transfer_state" $json = Invoke-RestMethod -Uri $state -Method Get -ContentType "application/json" -credential $RPCred | ConvertTo-Json $object = ConvertFrom-Json -InputObject $json $object.innerSet | select -Property sourceClusterName, groupName, transferState > $HOME\desktop\$clus.txt $CG = $object.innerSet | sort Name| select -First 5 foreach($_ in $CG){ if($_.transferState -eq "PAUSED" ){ $groupID = $_.consistencyGroupUID.trim("@{id=}") $data = @{ id = "$groupID" }; $body = $data | ConvertTo-Json; $transfer = "https://${clusipaddress}/fapi/rest/5_2/groups/$groupID/start_transfer" Invoke-RestMethod -Uri $transfer -Credential $RPCluster -Method PUT -ContentType "application/json" -Body $body -credential $RPCred } } } ################################################################# Stop-Transcript Copy-Item $HOME\Desktop\CGCreation-log-$global:DateTime.txt "Log collection location path" -Confirm:$false -Force Disconnect-VIServer * -Confirm:$false -Force
I have added replication step at the end of script.
Please do due diligence before running this RP4VM consistency group creation API script in the production environment. I am working on it to improve it, if you have any suggestions feel free to drop me a message or send me an email.