vSphere Replication (VR) 8.6 introduces REST API to allow automation of all operations and workflows.
Supported functionality through vSphere Replication REST API version 1.0 is:
- Session management
- Create/delete a site pairing
- Create/pause/delete/pause/resume a replication
- Querying vCenter inventory data, related to replication management
- Task monitoring
This blog post will demonstrate how to leverage the REST API to automate creating a VM replication through PowerShell script.
Details on the used APIs could be found here:
Configure Replication REST API
Semantics
Since vSphere Replication is about setting up replications between different sites (vCenter instances), there are some caveats. Some of the vSphere Replication workflows require requests to be made to different servers, e.g., local vCenter Server, remote vCenter Server, local VR Management Server (VRMS), remote VR Management Server.
VR REST API is designed to be used as a gateway to all those servers so that it can complete all workflows through single API connection. Therefore, for some endpoints there is a parameter to specify to which server the specific call needs to be made. For example, you can use the following request to get a list of datastores for local or remote vCenter Server instances.
GET <vr-rest-api>/api/rest/vr/v1/pairings/<pairing_id>/vcenters/<vcenter_id>/datastores
To support this, the API requires authentication to remote vCenter Server. Authentication is required only if a call to an API, including to a remote vCenter or to a remote VRMS, is made.
Configure VM Replication
Configure VM replication is by far the most complex and used workflow which you can do through VR REST API. Here is the step-by-step guidance on how to automate it through PowerShell script.
Configure VM replication is an operation between two sites – protected site and recovery site.
To configure a VM replication, we must have have the following data:
(1) Information about the VM – id, disk details, if suitable for replication
(2) Target datastore to place replication data
(3) (Optional) The target storage policy. If not specified, the default storage policy will be used.
(4) (Optional) The target vSphere Replication server to handle replication traffic. If not specified, it will be auto-selected by VRMS
(1) should be retrieved from the protected site
(2), (3), (4) should be retrieved from the recovery site.
In our example, we use the local site (which we are logged into) as protected source and the remote site as a recovery target.
Authentication
Let’s take a look at the following function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# Global variables $global:sessionID = "" function login() { $vcUser = Read-Host "Enter VC username" $vcPassword = Read-Host "Enter VC password" -AsSecureString $vcPassword = (New-Object PSCredential 0, $vcPassword).GetNetworkCredential().Password $authPair = "$($vcUser):$($vcPassword)" $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authPair)) $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("Content-Type", "application/json") $headers.Add("Authorization", "Basic $encodedCreds") $vrmsSessionApiUri = "https://" + $vrmsServer + "/api/rest/vr/v1/session" "Authenticating into [$vrmsSessionApiUri]" try { $response = Invoke-RestMethod $vrmsSessionApiUri -Method 'POST' -Headers $headers } catch { $_.Exception.Response Exit 1 } $global:sessionID = $response.session_id "Session ID for vSphere Replication REST API is: $global:sessionID" $global:headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $global:headers.Add("x-dr-session", "$global:sessionID") $global:headers.Add("Content-Type", "application/json") } |
We ask the user for the local vCenter’s user name and password. Then constructing Authorization header with them in Base64 format and making a POST
request to /session
endpoint.
If successful – we construct global variable with the x-dr-session
header and the session ID as a value. From now on, if we use $global:headers
every request to the API will be authenticated until the session expires due to inactivity.
Retrieve and find a pairing
VR REST API is organized in pairings between two sites. All APIs related to replications are available within a pairing, thus we would need to find the desired pairing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# Global variables $global:pairing = $null function findPairing($remoteVcId) { "Search for pairing with remote VC ID [$remoteVcId]" $vrmsPairingsApiUri = "https://" + $vrmsServer + "/api/rest/vr/v1/pairings" "Get all pairings at [$vrmsPairingsApiUri]" try { $response = Invoke-RestMethod $vrmsPairingsApiUri -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response Exit 1 } $pairings = $response.list "Retrieved [$($pairings.Count)] pairings" foreach ($item in $pairings) { if ($remoteVcId -eq $item.remote_vc_server.id) { $global:pairing = $item } } if (!$global:pairing) { "Cannot find pairing!" Exit 1 } "**********************" "Pairing is found!" "Pairing ID: $($global:pairing.pairing_id)" "Local vCenter ID: $($global:pairing.local_vc_server.id)" "Local vCenter name: $($global:pairing.local_vc_server.name)" "Remote vCenter ID: $($global:pairing.remote_vc_server.id)" "Remote vCenter name: $($global:pairing.remote_vc_server.name)" "Local VRMS: $($global:pairing.local_vrms.id)" "Remote VRMS: $($global:pairing.remote_vrms.id)" "**********************" } |
This function fetches all pairings and searches for a pairing with a given remote vCenter ID. If the remote vCenter ID is unknown, we could list all pairings and choose based on the vCenter or VRMS hostname.
Great! We have session and we know which pairing we want to use for our VM replication. Next API calls, however, require authentication to the remote site.
Establishing session to remote site
Pairing consists of two sites – local and remote. When we authenticate to the VR REST API, we create a session to the local site. However, authentication to the remote site is explicit operation per pairing. Once authenticated to both sites, all APIs are available.
This is a function which does remote login with user-supplied credentials:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function remoteLogin($pairingId) { "Logging into remote site" $remoteVcUser = Read-Host "Enter remote VC username" $remoteVcPassword = Read-Host "Enter remote VC password" -AsSecureString $remoteVcPassword = (New-Object PSCredential 0, $remoteVcPassword).GetNetworkCredential().Password $remoteLoginApiUri = "https://" + $vrmsServer + "/api/rest/vr/v1/pairings/$pairingId/remote-session" $authPair = "$($remoteVcUser):$($remoteVcPassword)" $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authPair)) $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("x-dr-session", "$global:sessionID") $headers.Add("Authorization", "Basic $encodedCreds") try { Invoke-RestMethod $remoteLoginApiUri -Method 'POST' -Headers $headers } catch { $_.Exception.Response Exit 1 } "**********************" "Remote login is successful!" "**********************" } |
Important thing in the implementation is that we must provide both x-dr-session
AND Authorization
header. What this does in the background is to associate the session to the remote site with the current client session. We can continue to use x-dr-session
header with the same session id value as before.
Find target datastore
The ID of a target datastore is a requirement of the replication spec, used to configure VM for replication. Target datastore is a datastore at the recovery site (our remote site) and is used as a place to put the replicated VM files (VM home, disks, etc). Here is the function to find the target datastore ID by a given name:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# Global variables $global:targetDatastoreId = $null function findTargetDatastore($targetDatastoreName) { "Search for target datastore ID" $datastoresApiURI = "https://" + $vrmsServer + "/api/rest/vr/v1/pairings/" + $global:pairing.pairing_id + "/vcenters/" + $global:pairing.remote_vc_server.id + "/datastores?filter_property=name&filter=$targetDatastoreName" "Query [$datastoresApiURI]" try { $response = Invoke-RestMethod $datastoresApiURI -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response Exit 1 } "Retrieved [$($response.list.Count)] datastores" foreach ($record in $response.list) { if ($record.name -eq $targetDatastoreName) { $global:targetDatastoreId = $record.id } } "vSphere Target Datastore ID: $global:targetDatastoreId" } |
Here we use built-in filtering functionality of the REST API to filter datastores by name:
?filter_property=name&filter=$targetDatastoreName
Find target storage policy
Target storage policy is used to provision the VM disk replica. It’s an optional parameter and if not provided, the datastore’s default storage policy will be used. If the target datastore does not comply with the target storage policy, there might be a replication error. We can check compliance with a call to:
POST /{pairing_id}/vcenters/{vcenter_id}/storage-policies/{storage_policy_id}/actions/check-compliance
when setting the target datastore ID as body to the request.
This is the function which retrieves the target storage policy ID:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#Global variables $global:targetStoragePolicyId = $null function findStoragePolicy($storagePolicyName) { if ($storagePolicyName -eq $null) { "No target storage policy specified. Will use default storage policy." return } "Search for target storage policy ID" $storagePolicyApiURI = "https://" + $vrmsServer + "/api/rest/vr/v1/pairings/" + $global:pairing.pairing_id + "/vcenters/" + $global:pairing.remote_vc_server.id + "/storage-policies?filter_property=storage_policy_name&filter=$storagePolicyName" "Query [$storagePolicyApiURI]" try { $response = Invoke-RestMethod $storagePolicyApiURI -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response Exit 1 } "Retrieved [$($response.list.Count)] storage policies" foreach ($record in $response.list) { if ($record.storage_policy_name -eq $storagePolicyName) { $global:targetStoragePolicyId = $record.storage_policy_id } } "vSphere Storage Policy ID: $global:targetStoragePolicyId" } |
Retrieve details about the VMs to replicate
There are different approaches to collect the VMs data depending on what we know about the VMs we want to replicate. In our example, we will search VMs based on their name. Keep in mind that the filtering we use is more like “value contains” rather than “value is“. So, if we have VMs with similar names, we might get all of them based on the filtering criteria.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
function collectVmData($vmNames) { if ($vmNames -eq $null -Or $vmNames.Length -eq 0) { "No VMs selected for replication!" return } "Constructing VM specs for [$vmNames]" $vmFilter="" foreach ($vm in $vmNames) { $vmFilter = $vmFilter + "filter=$vm" $vmFilter = $vmFilter + "&" } $vmFilter = $vmFilter.Substring(0, $vmFilter.Length - 1) $vmDetailsApiURI = "https://" + $vrmsServer + "/api/rest/vr/v1/pairings/" + $global:pairing.pairing_id + "/vcenters/" + $global:pairing.local_vc_server.id + "/vms?suitable_for_replication=true&filter_property=name&$vmFilter" "Query [$vmDetailsApiURI]" try { $response = Invoke-RestMethod $vmDetailsApiURI -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response Exit 1 } "Retrieved [$($response.list.Count)] virtual machines" "********* VM name to ID **********" foreach ($vm in $response.list) { $vm.name + " ------> " + $vm.id } if (!($response.list.Count -eq $vmNames.Length)) { "WARNING: Not all VMs are suitable for replication or existent!" } "**********************" $global:vmsData = $response.list } |
Note we are using local_vc_server.id
to collect VMs data from local site (as it’s our protected site).
Also, note we use suitable_for_replication=true
query parameter to filter VMs automatically, which excludes VMs which are already replicated, missing permissions, templates, special VMs, etc.
If there is a VM which is not suitable for replication, but you don’t know the reason – use
GET /{pairing_id}/vcenters/{vcenter_id}/vms/{vm_id}/capability
in addition, it will return a detailed information about the supported replication settings. This API is used for suitable VMs as well.
Configure replication
Finally! We are done with collecting data and will replicate our VMs!
The function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
$autoReplicateNewDisks = $true $rpo = 10 $lwdEncryptionEnabled = $false $mpitDays= 0 $mpitInstances= 0 $mpitEnabled= $false $networkCompressionEnabled = $false $quiesceEnabled = $false $destinationDiskFormat = "SAME_AS_SOURCE" function configureReplication($vmsData) { $specs = @() foreach ($vm in $vmsData) { $spec = [PSCustomObject]@{ auto_replicate_new_disks = $autoReplicateNewDisks rpo = $rpo lwd_encryption_enabled = $lwdEncryptionEnabled mpit_days = $mpitDays mpit_enabled = $mpitEnabled mpit_instances = $mpitInstances network_compression_enabled = $networkCompressionEnabled quiesce_enabled = $quiesceEnabled vm_id = $vm.id target_replication_server_id = $null target_vc_id = $global:pairing.remote_vc_server.id disks = @() } foreach ($dsk in $vm.disks) { $disk_data = [PSCustomObject]@{ destination_datastore_id = $global:targetDatastoreId destination_disk_format = $destinationDiskFormat enabled_for_replication = "true" use_seeds = "false" # We already have the disk information from the previous call vm_disk = $dsk } $spec.disks += $disk_data } $specs += $spec } if ($specs.Length -eq 0) { "No VMs can be configured for replication! Aborting.." Exit 1 } "Running configure VM replication for [$($specs.Length)]" $jsonBody = ConvertTo-Json -InputObject $specs -Depth 100 $configureReplicationApiUri = "https://" + $vrmsServer + "/api/rest/vr/v1/pairings/" + $global:pairing.pairing_id + "/replications" "Query [$configureReplicationApiUri]" try { $response = Invoke-RestMethod $configureReplicationApiUri -Method 'POST' -Headers $headers -Body "$jsonBody" } catch { "Failed to configure replication!" $_.Exception.Response Exit 1 } "Configure VM replication successfully called!" } |
First, we construct configure replication spec for each VM with the desired settings. Then for each VM disk construct replication structure with the target datastore ID and target storage policy ID. VM disk data is already available from the previous call and just set it back.
Note that target_vc_id
is the recovery site (in our example – the remote site).
POST
request responds with a list of tasks for each VM which can be monitored to know the outcome of the operation.
Configure VM replication API has all settings which are available through the UI – excluding disks, MPIT, encryption, selection of seeds, etc. Some of these require additional API calls to collect data.
Logout
We recommend to log out from the VR REST API after you are finished with the main logic. This way you won’t exhaust the session pool to any of the services.
1 2 3 4 5 6 7 8 |
function logout() { "Logging out of VR REST API" $vrmsSessionApiUri = "https://" + $vrmsServer + "/api/rest/vr/v1/session" Invoke-RestMethod $vrmsSessionApiUri -Method 'DELETE' -Headers $global:headers $global:headers = $null } |
Put it all together
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
<# Script to configure VM replication through vSphere Replication REST API: - Authentication to VRMS - Retrieving existing pairings - Remote login - Retrieving VMs suitable for replication - Query recovery datastores and filter by datastore name - Query recovery storage policies - Prepare VM data to configure replication - Calling create replication - Logout #> param ([Parameter(Mandatory)] [string]$vrmsServer, [string]$remoteVcId, [Parameter(Mandatory)] [string]$targetDatastoreName, # If null - will use the default storage policy [string]$targetStoragePolicyName=$null, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [String[]] $vmNames, [int]$rpo=10, [int]$mpitInstances=0, [int]$mpitDays=0, [boolean]$mpitEnabled=$false, [boolean]$networkCompressionEnabled=$false, [boolean]$lwdEncryptionEnabled=$false, [boolean]$quiesceEnabled=$false, [boolean]$autoReplicateNewDisks=$true, [string]$destinationDiskFormat="SAME_AS_SOURCE", [boolean]$trace=$false) $timestamp = Get-Date -f "MM-dd-yyyy-HH.mm.ss" $output_log = ".\Logs\ConfigureReplicationBlog-Log"+$timestamp+".log" Start-Transcript -Path "C:\transcripts\$output_log" -NoClobber # Settings $printResponses = $trace $vrmsApiVersion = "v1" $vrmsApiUri = "https://" + $vrmsServer + "/api/rest/vr/" + $vrmsApiVersion $vrmsSessionApiUri = $vrmsApiUri + "/session" $vrmsPairingsApiUri = $vrmsApiUri + "/pairings" # Global variables $global:sessionID = "" $global:pairing = $null $global:headers = $null $global:targetDatastoreId = $null $global:targetStoragePolicyId = $null $global:vmsData = $null function login() { $vcUser = Read-Host "Enter VC username" $vcPassword = Read-Host "Enter VC password" -AsSecureString $vcPassword = (New-Object PSCredential 0, $vcPassword).GetNetworkCredential().Password $authPair = "$($vcUser):$($vcPassword)" $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authPair)) $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("Content-Type", "application/json") $headers.Add("Authorization", "Basic $encodedCreds") "Authenticating into [$vrmsSessionApiUri]" try { $response = Invoke-RestMethod $vrmsSessionApiUri -Method 'POST' -Headers $headers } catch { $_.Exception.Response Exit 1 } $global:sessionID = $response.session_id "Session ID for vSphere Replication REST API is: $global:sessionID" $global:headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $global:headers.Add("x-dr-session", "$global:sessionID") $global:headers.Add("Content-Type", "application/json") $response = Invoke-RestMethod $vrmsSessionApiUri -Method 'GET' -Headers $global:headers printAsJson($response) } function disableSecurityChecks() { "Disabling Certificate Checks" [System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy } function remoteLogin($pairingId) { "Logging into remote site" $remoteVcUser = Read-Host "Enter remote VC username" $remoteVcPassword = Read-Host "Enter remote VC password" -AsSecureString $remoteVcPassword = (New-Object PSCredential 0, $remoteVcPassword).GetNetworkCredential().Password $remoteLoginApiUri= $vrmsApiUri + "/pairings/$pairingId/remote-session" $authPair = "$($remoteVcUser):$($remoteVcPassword)" $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authPair)) $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("x-dr-session", "$global:sessionID") $headers.Add("Authorization", "Basic $encodedCreds") try { Invoke-RestMethod $remoteLoginApiUri -Method 'POST' -Headers $headers } catch { $_.Exception.Response logout Exit 1 } "**********************" "Remote login is successful!" "**********************" } function logout() { "Logging out of VRMS REST API" Invoke-RestMethod $vrmsSessionApiUri -Method 'DELETE' -Headers $global:headers $global:headers = $null } function findPairing($remoteVcId) { "Search for pairing with remote VC ID [$remoteVcId]" "Get all pairings at [$vrmsPairingsApiUri]" try { $response = Invoke-RestMethod $vrmsPairingsApiUri -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response logout Exit 1 } $pairings = $response.list "Retrieved [$($pairings.Count)] pairings" if ($remoteVcId -eq $null -Or $remoteVcId -eq "") { # List all pairings and stop "remoteVcId not specified. Select from the list:" foreach ($item in $pairings) { "*****" "Pairing ID: $($item.pairing_id)" "Local vCenter ID: $($item.local_vc_server.id)" "Local vCenter name: $($item.local_vc_server.name)" "Remote vCenter ID: $($item.remote_vc_server.id)" "Remote vCenter name: $($item.remote_vc_server.name)" "Local VRMS: $($item.local_vrms.id)" "Remote VRMS: $($item.remote_vrms.id)" "*****" } $remoteVcId = Read-Host "Enter remote vCenter ID" } foreach ($item in $pairings) { if ($remoteVcId -eq $item.remote_vc_server.id) { $global:pairing = $item } } if (!$global:pairing) { "Cannot find pairing!" logout Exit 1 } "**********************" "Pairing is found!" "Pairing ID: $($global:pairing.pairing_id)" "Local vCenter ID: $($global:pairing.local_vc_server.id)" "Local vCenter name: $($global:pairing.local_vc_server.name)" "Remote vCenter ID: $($global:pairing.remote_vc_server.id)" "Remote vCenter name: $($global:pairing.remote_vc_server.name)" "Local VRMS: $($global:pairing.local_vrms.id)" "Remote VRMS: $($global:pairing.remote_vrms.id)" "**********************" } function findTargetDatastore($targetDatastoreName) { "Search for target datastore ID" $datastoresApiURI = $vrmsApiUri + "/pairings/" + $global:pairing.pairing_id + "/vcenters/" + $global:pairing.remote_vc_server.id + "/datastores?filter_property=name&filter=$targetDatastoreName" "Query [$datastoresApiURI]" try { $response = Invoke-RestMethod $datastoresApiURI -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response logout Exit 1 } "Retrieved [$($response.list.Count)] datastores" printAsJson($response) foreach ($record in $response.list) { if ($record.name -eq $targetDatastoreName) { $global:targetDatastoreId = $record.id } } "vSphere Target Datastore ID: $global:targetDatastoreId" } function findStoragePolicy($storagePolicyName) { if ($storagePolicyName -eq $null) { "No target storage policy specified. Will use default storage policy." return } "Search for target storage policy ID" $storagePolicyApiURI = $vrmsApiUri + "/pairings/" + $global:pairing.pairing_id + "/vcenters/" + $global:pairing.remote_vc_server.id + "/storage-policies?filter_property=storage_policy_name&filter=$storagePolicyName" "Query [$storagePolicyApiURI]" try { $response = Invoke-RestMethod $storagePolicyApiURI -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response logout Exit 1 } "Retrieved [$($response.list.Count)] storage policies" foreach ($record in $response.list) { if ($record.storage_policy_name -eq $storagePolicyName) { $global:targetStoragePolicyId = $record.storage_policy_id } } "vSphere Storage Policy ID: $global:targetStoragePolicyId" } function collectVmData($vmNames) { if ($vmNames -eq $null -Or $vmNames.Length -eq 0) { "No VMs selected for replication!" return } "Constructing VM specs for [$vmNames]" $vmFilter="" foreach ($vm in $vmNames) { $vmFilter = $vmFilter + "filter=$vm" $vmFilter = $vmFilter + "&" } $vmFilter = $vmFilter.Substring(0, $vmFilter.Length - 1) $vmDetailsApiURI = $vrmsApiUri + "/pairings/" + $global:pairing.pairing_id + "/vcenters/" + $global:pairing.local_vc_server.id + "/vms?suitable_for_replication=true&filter_property=name&$vmFilter" "Query [$vmDetailsApiURI]" try { $response = Invoke-RestMethod $vmDetailsApiURI -Method 'GET' -Headers $global:headers } catch { $_.Exception.Response logout Exit 1 } "Retrieved [$($response.list.Count)] virtual machines" "********* VM name to ID **********" foreach ($vm in $response.list) { $vm.name + " ------> " + $vm.id } if (!($response.list.Count -eq $vmNames.Length)) { "WARNING: Not all VMs are suitable for replication or existent!" } "**********************" $global:vmsData = $response.list } function configureReplication($vmsData) { $specs = @() foreach ($vm in $vmsData) { $spec = [PSCustomObject]@{ auto_replicate_new_disks = $autoReplicateNewDisks rpo = $rpo lwd_encryption_enabled = $lwdEncryptionEnabled mpit_days = $mpitDays mpit_enabled = $mpitEnabled mpit_instances = $mpitInstances network_compression_enabled = $networkCompressionEnabled quiesce_enabled = $quiesceEnabled vm_id = $vm.id target_replication_server_id = $null target_vc_id = $global:pairing.remote_vc_server.id disks = @() } foreach ($dsk in $vm.disks) { $disk_data = [PSCustomObject]@{ destination_datastore_id = $global:targetDatastoreId destination_storage_policy_id = $global.targetStoragePolicyId destination_disk_format = $destinationDiskFormat enabled_for_replication = "true" use_seeds = "false" # We already have the disk information from the previous call vm_disk = $dsk } $spec.disks += $disk_data } $specs += $spec } if ($specs.Length -eq 0) { "No VMs can be configured for replication! Aborting.." logout Exit 1 } "Running configure VM replication for [$($specs.Length)]" printAsJson($specs) $jsonBody = ConvertTo-Json -InputObject $specs -Depth 100 $configureReplicationApiUri = $vrmsApiUri + "/pairings/" + $global:pairing.pairing_id + "/replications" "Query [$configureReplicationApiUri]" try { $response = Invoke-RestMethod $configureReplicationApiUri -Method 'POST' -Headers $headers -Body "$jsonBody" } catch { "Failed to configure replication!" $_.Exception.Response logout Exit 1 } "Configure VM replication successfully called!" printAsJson($response) } function printAsJson($element) { if ($printResponses) { $jsonItem = ConvertTo-Json -InputObject $element -Depth 100 "$jsonItem" } } function main() { disableSecurityChecks login findPairing($remoteVcId) remoteLogin($global:pairing.pairing_id) findTargetDatastore($targetDatastoreName) findStoragePolicy($targetStoragePolicyName) collectVmData($vmNames) printAsJson($vmsData) configureReplication($vmsData) logout } # Call main function main Stop-Transcript |
We have combined all functions into a self-contained script. Sample usage of the script:.\vr-configure-replication.ps1 -vrmsServer my-vrms-instance.acme.com -vmNames myVm -autoReplicateNewDisks $true -targetDatastoreName local
Related Posts
Also check PowerCLI blog posts related to VR and SRM:
Follow the VMware DR Community Team on Twitter @VMware_DR_team and send us any feedback.