Share

Preserving OVF Properties Across Failover Using Orchestrator

by Mayank Goyal · 9 Jan 2026

One of my team members discovered this information, and we were quite surprised to learn that such a limitation exists in SRM, of which we were previously unaware.

Overview

When using SRM / Live Recovery in VMware Cloud Foundation, OVF (vApp) properties are not preserved automatically during failover. This is a known limitation and is documented by Broadcom here.

Document titled 'Back up OVF Properties of Protected Virtual Machines in the Protected VMware Cloud Foundation Instance' with instructions on how to save OVF properties for virtual machines.

You have to manually record and save them which can be error-prone and a tedious task to do, especially when there are lot of VMs to cover.

To address this, I’ve built a Aria Automation Orchestrator (vRO) workflow that:

  1. Extracts OVF properties from one or more VMs provided as workflow input
  2. Reconstructs the real OVF keys correctly (using classId, id and instanceId)
  3. Exports them into a CSV
  4. Stores the CSV as a Resource Element with a timestamp for audit and restore later

Workflow Process

Things to check

  • vCenter plugin must be configured in Orchestrator where protected VMs exist.
  • This code is tested on Aria Automation Orchestrator 8.18.1 embedded.
  • You can get the workflow package here and import in your environment before we start.

Running the workflow in vRO

After importing the package (how? learn here), go to the workflow “Backup VM OVF Properties as CSV” and enter vm names in the field value to find and select protected VMs. As you can see here, I have selected my Aria Automation, Aria Operations, Aria Suite Lifecycle and Workspace ONE Access appliance VMs.

Click Run and wait for the workflow to complete. You will see in the logs, which VM is processed and has how many OVF properties to preserve.

and also the creation of a resource element with date time stamp saved in ovf-backup folder with name ovf-properties_<timestamp>.csv as shown in logs below.

2026-01-08 16:02:34.123 +00:00 INFO === OVF EXPORT TASK STARTED ===
2026-01-08 16:02:34.131 +00:00 INFO Starting OVF property extraction for VMs
2026-01-08 16:02:34.133 +00:00 INFO Processing VM: wsone1
2026-01-08 16:02:34.137 +00:00 INFO Found 11 OVF properties for VM: wsone1
2026-01-08 16:02:34.140 +00:00 INFO Processing VM: vra1
2026-01-08 16:02:34.142 +00:00 INFO Found 15 OVF properties for VM: vra1
2026-01-08 16:02:34.145 +00:00 INFO Processing VM: vrops1
2026-01-08 16:02:34.149 +00:00 INFO Found 16 OVF properties for VM: vrops1
2026-01-08 16:02:34.152 +00:00 INFO Processing VM: lcm
2026-01-08 16:02:34.154 +00:00 INFO Found 16 OVF properties for VM: lcm
2026-01-08 16:02:34.156 +00:00 INFO OVF property extraction completed for all VMs
2026-01-08 16:02:34.157 +00:00 INFO === OVF EXPORT TASK COMPLETED ===
2026-01-08 16:02:34.170 +00:00 INFO === RESOURCE ELEMENT CREATION TASK STARTED ===
2026-01-08 16:02:34.172 +00:00 INFO Generated timestamp: 2026-01-08_16-02-34
2026-01-08 16:02:34.174 +00:00 INFO Prepared Resource Element content: ovf-properties_2026-01-08_16-02-34.csv
2026-01-08 16:02:34.182 +00:00 INFO Using existing Resource Element Category: ovf-backup
2026-01-08 16:02:34.183 +00:00 INFO Creating Resource Element: ovf-backup/ovf-properties_2026-01-08_16-02-34.csv
2026-01-08 16:02:34.275 +00:00 INFO === RESOURCE ELEMENT CREATION TASK COMPLETED ===
Screenshot showing the resources in vRO with CSV files for OVF properties listed in a directory.

Go to Resource Elements tab and locate the newly created CSV resource element and export it. If you want, you can verify the entries from vCenter by selecting VM object -> Configure -> vApp Options -> OVF Settings -> View OVF Environment.

Reference Code

Export OVF properties

/**
 * Export OVF (vApp) properties for multiple VMs into CSV
 *
 * @param {VC:VirtualMachine[]} arrVirtualMachine
 * @return {string} CSV output
 */

System.log("=== OVF EXPORT TASK STARTED ===");

csvContent = exportOvfPropertiesToCsv(arrVirtualMachine);

System.log("=== OVF EXPORT TASK COMPLETED ===");

function exportOvfPropertiesToCsv(arrVirtualMachine) {

	System.log("Starting OVF property extraction for VMs");

	if (!arrVirtualMachine || arrVirtualMachine.length === 0) {
		throw "arrVirtualMachine is empty or null";
	}

	var csvLines = [];
	csvLines.push("vmName,ovfKey,value,defaultValue,category,type");

	for (var vmIndex = 0; vmIndex < arrVirtualMachine.length; vmIndex++) {

		var vm = arrVirtualMachine[vmIndex];
		var vmName = vm.name;

		System.log("Processing VM: " + vmName);

		if (!vm.config ||
		    !vm.config.vAppConfig ||
		    !vm.config.vAppConfig.property ||
		    vm.config.vAppConfig.property.length === 0) {

			System.warn("No OVF properties found for VM: " + vmName);
			continue;
		}

		var properties = vm.config.vAppConfig.property;
		System.log("Found " + properties.length + " OVF properties for VM: " + vmName);

		for (var i = 0; i < properties.length; i++) {

			var prop = properties[i];

			var ovfKey        = buildOvfKey(prop);
			var value         = prop.value || "";
			var defaultValue  = prop.defaultValue || "";
			var category      = prop.category || "";
			var type          = prop.type || "";

			// Normalize VMware enum quoting first
			value = normalizeQuotes(value);

			// CSV escaping
			ovfKey       = escapeCsv(ovfKey);
			value        = escapeCsv(value);
			defaultValue = escapeCsv(defaultValue);
			category     = escapeCsv(category);
			type         = escapeCsv(type);

			csvLines.push(
				vmName + "," +
				ovfKey + "," +
				value + "," +
				defaultValue + "," +
				category + "," +
				type
			);
		}
	}

	System.log("OVF property extraction completed for all VMs");
	return csvLines.join("\n");
}

/**
 * Build the real OVF key:
 * classId.id.instanceId (only if parts exist)
 */
function buildOvfKey(prop) {

	var id = prop.id || "";
	var classId = prop.classId || "";
	var instanceId = prop.instanceId || "";

	if (classId && instanceId) {
		return classId + "." + id + "." + instanceId;
	}

	if (classId) {
		return classId + "." + id;
	}

	return id;
}

/**
 * Normalize duplicated escaped quotes ("") โ†’ (")
 */
function normalizeQuotes(value) {
	return value.replace(/""/g, "\"");
}

/**
 * CSV escape helper (RFC-4180 compliant)
 */
function escapeCsv(value) {
	if (value.indexOf(",") !== -1 || value.indexOf("\"") !== -1) {
		return "\"" + value.replace(/"/g, "\"\"") + "\"";
	}
	return value;
}

Create CSV Resource element

System.log("=== RESOURCE ELEMENT CREATION TASK STARTED ===");

// Build human-readable timestamp
var now = new Date();

function pad(n) {
	return (n < 10) ? "0" + n : n;
}

var timestamp =
	now.getFullYear() + "-" +
	pad(now.getMonth() + 1) + "-" +
	pad(now.getDate()) + "_" +
	pad(now.getHours()) + "-" +
	pad(now.getMinutes()) + "-" +
	pad(now.getSeconds());

System.log("Generated timestamp: " + timestamp);

// Create MIME attachment from CSV
var mime = new MimeAttachment();

var name = "ovf-properties_" + timestamp + ".csv";
mime.name = name;
mime.mimeType = "text/csv";
mime.content = csvContent;

System.log("Prepared Resource Element content: " + name);

// Get or create Resource Element Category
var path = "ovf-backup";
var resourceCat = Server.getResourceElementCategoryWithPath(path);

if (!resourceCat) {
	System.log("Resource Element Category not found, creating: " + path);
	resourceCat = Server.createResourceElementCategory(path);
} else {
	System.log("Using existing Resource Element Category: " + path);
}

// Always create a new Resource Element
System.log("Creating Resource Element: " + path + "/" + name);
Server.createResourceElement(path, name, mime);

System.log("=== RESOURCE ELEMENT CREATION TASK COMPLETED ===");

Developer Note

1. prop.key vs prop.id

  • prop.key is a numeric internal identifier
  • It does not match the OVF XML key

2. Reconstructing exact OVF Key

OVF XML keys like: vami.DNS.IdentityManager

Are represented in vSphere as:

  • classId = vami
  • id = DNS
  • instanceId = IdentityManager

Hence, id alone would not work and needed to be reconstructed in the code.

Conclusion

With this approach we now have:

  • Deterministic OVF properties preservation with no human-errors
  • CSV-based backups for DR
  • Timestamped, auditable artifacts
  • A clean foundation for post-failover restore automation

The next logical step is to read this CSV and reapply OVF properties on the recovered VMs, which can be done using ReconfigVM_Task. You can also configure to trigger vRO Workflow as part of SRM failover as shown in this article Triggering vRO Workflow with SRM Recovery Plan by Sam Perrin.

A screenshot of a recovery step interface in VMware, showing a list of actions including 'Synchronize storage', 'Restore recovery site hosts from standby', and 'Command: vRO Workflow' highlighted in red.

๐Ÿ“ฆDownload package from GitHub

Here is the download link.

If you want, I can write Part 2 of this blog covering:

  • CSV parsing
  • Reapplying OVF properties
  • Validation after recovery

Just say the word.


Discover more from Cloud Blogger

Subscribe to get the latest posts sent to your email.

You may also like