<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Cosmin.us</title><link>https://cosmin.us/</link><description>Recent content on Cosmin.us</description><generator>Hugo</generator><language>en-US</language><dc:creator>Cosmin Trif</dc:creator><lastBuildDate>Fri, 07 Aug 2026 09:00:00 +0000</lastBuildDate><atom:link href="https://cosmin.us/index.xml" rel="self" type="application/rss+xml"/><item><title>Deploying a VCF 9.1 Fleet with the VMware Cloud Foundation Installer: Step-by-Step Instructions</title><link>https://cosmin.us/deploying-a-vcf-9-1-fleet-with-the-vmware-cloud-foundation-installer-step-by-step-instructions/</link><pubDate>Fri, 07 Aug 2026 09:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/deploying-a-vcf-9-1-fleet-with-the-vmware-cloud-foundation-installer-step-by-step-instructions/</guid><description>&lt;p&gt;Last year, when VCF 9.0 shipped, I covered the VCF Installer in three posts: &lt;a href="https://cosmin.us/deploying-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;deploying the appliance&lt;/a&gt;, &lt;a href="https://cosmin.us/configuring-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;configuring it&lt;/a&gt;, and &lt;a href="https://cosmin.us/deploy-a-vcf-9-instance-in-an-existing-vcf-fleet/"&gt;deploying a VCF 9 instance&lt;/a&gt;. With VCF 9.1 &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes.html"&gt;generally available since May 12, 2026&lt;/a&gt;, it was time to run the whole flow again in the lab — and enough has changed that it earns a fresh walkthrough. This post covers the complete path in one go: downloading and deploying the 9.1 installer appliance, connecting the depot, downloading binaries, and deploying a new VCF fleet on top of an existing vCenter. I used the 9.1.0.0400 installer from the July 2026 refresh.&lt;/p&gt;</description><content:encoded>&lt;p&gt;Last year, when VCF 9.0 shipped, I covered the VCF Installer in three posts: &lt;a href="https://cosmin.us/deploying-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;deploying the appliance&lt;/a&gt;, &lt;a href="https://cosmin.us/configuring-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;configuring it&lt;/a&gt;, and &lt;a href="https://cosmin.us/deploy-a-vcf-9-instance-in-an-existing-vcf-fleet/"&gt;deploying a VCF 9 instance&lt;/a&gt;. With VCF 9.1 &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes.html"&gt;generally available since May 12, 2026&lt;/a&gt;, it was time to run the whole flow again in the lab — and enough has changed that it earns a fresh walkthrough. This post covers the complete path in one go: downloading and deploying the 9.1 installer appliance, connecting the depot, downloading binaries, and deploying a new VCF fleet on top of an existing vCenter. I used the 9.1.0.0400 installer from the July 2026 refresh.&lt;/p&gt;
&lt;h2 id="what-changed-since-the-90-posts"&gt;What changed since the 9.0 posts&lt;/h2&gt;
&lt;p&gt;The overall shape is the same — deploy the OVA, connect a depot, download binaries, run the deployment wizard — but 9.1 moves a few important pieces, per the official &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/what-s-new/whats-new-installer.html"&gt;What&amp;rsquo;s New for the VCF Installer&lt;/a&gt; and the depot KBs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The download token is gone.&lt;/strong&gt; Depot authentication now uses a &lt;strong&gt;Download Service ID + Activation Code&lt;/strong&gt; obtained from the VCF Business Services console. The token flow from my 9.0 configuration post no longer applies — see &lt;a href="https://knowledge.broadcom.com/external/article/443647/download-token-has-been-replaced-by-acti.html"&gt;KB 443647&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VCF Management Services are deployed by default.&lt;/strong&gt; The installer now provisions the VCF services runtime, fleet lifecycle, identity broker, software depot, Salt RaaS, telemetry, and the centralized &lt;strong&gt;License Server&lt;/strong&gt; as part of a new deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integrated planning workflow.&lt;/strong&gt; The wizard generates CPU, memory, storage, VLAN, and FQDN requirements up front and validates your infrastructure against them — including a &lt;strong&gt;Pre-fill generated FQDNs&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-generated passwords.&lt;/strong&gt; The installer can generate the component passwords for you, retrievable after deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dual-stack support.&lt;/strong&gt; The appliance OVA now asks for an IP Address Version (IPv4 only, or IPv4 and IPv6), and existing dual-stack environments are supported — though wizard-driven deployments on an existing vCenter remain IPv4-only; dual-stack goes through a JSON spec.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lowercase FQDNs are now formalized.&lt;/strong&gt; The 9.1 planning docs state &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/planning-and-preparation/vcf-components-fqdns-and-ip-addresses/first-vcf-instance-fqdns-and-ip-addresses.html"&gt;do not use capital letters in the FQDN&lt;/a&gt;, and uppercase component FQDNs are a documented deployment-failure &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/known-issues/vcf-installer-91-known-issues.html"&gt;known issue&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="download-the-vcf-installer"&gt;Download the VCF Installer&lt;/h2&gt;
&lt;p&gt;The appliance OVA comes from the Broadcom Support Portal at &lt;a href="https://support.broadcom.com/web/ecx"&gt;support.broadcom.com&lt;/a&gt; — select the &lt;strong&gt;VMware Cloud Foundations&lt;/strong&gt; division, go to &lt;strong&gt;My Downloads&lt;/strong&gt;, and pick &lt;strong&gt;VMware Cloud Foundation 9&lt;/strong&gt; with your target release. If the portal navigation fights you, &lt;a href="https://knowledge.broadcom.com/external/article/401497/vmware-vcf-90-or-vvf-90-downloads-in-the.html"&gt;KB 401497&lt;/a&gt; walks through it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the release — I grabbed &lt;strong&gt;9.1.0.0400&lt;/strong&gt; from the July 2026 patch wave&lt;/li&gt;
&lt;li&gt;Agree to the Terms and Conditions&lt;/li&gt;
&lt;li&gt;Download &lt;strong&gt;VCF-SDDC-Manager-Appliance-9.1.0.0400.25570100.ova&lt;/strong&gt; (2.28 GB) and verify the checksum&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/001-capture-001.webp" alt="Broadcom support portal showing the VCF Installer 9.1.0.0400 download page with the VCF-SDDC-Manager-Appliance OVA file, build number 25570100, and its SHA2 and MD5 checksums." width="1649" height="588" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="deploy-the-vcf-installer-appliance"&gt;Deploy the VCF Installer appliance&lt;/h2&gt;
&lt;p&gt;Sizing is unchanged from 9.0: 4 vCPUs, 16 GB RAM, and 914 GB storage (thin provisioning keeps that honest), per the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/preparing-your-environment/deploy-the-vmware-cloud-foundation-installer-appliance.html"&gt;official deployment page&lt;/a&gt;. The docs describe two placement options: on one of the ESX hosts that will form the management domain (the appliance is converted into SDDC Manager during deployment), or outside the management infrastructure entirely, where one appliance can deploy multiple platforms. As in the 9.0 post, I am deploying the OVA through an existing vCenter.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to vCenter with a user that can deploy an OVF&lt;/li&gt;
&lt;li&gt;Right click the cluster and click &lt;strong&gt;Deploy OVF Template&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/002-capture-002.webp" alt="vSphere Client actions menu on cluster mgmt-cluster-01 showing the Deploy OVF Template option." width="488" height="256" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Local file&lt;/strong&gt; and upload the installer OVA&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/003-capture-003.webp" alt="Deploy OVF Template wizard step 1 with the local file VCF-SDDC-Manager-Appliance-9.1.0.0400.25570100.ova selected." width="1148" height="610" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Give the appliance a name&lt;/li&gt;
&lt;li&gt;Select a deployment location&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/004-capture-004.webp" alt="Deploy OVF Template wizard step 2 with the VM name set and mgmt-datacenter-01 selected as the target location under vcenter-mgmt.vcf.sddc.lab." width="1143" height="607" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select a compute resource and wait for the compatibility checks&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/005-capture-005.webp" alt="Deploy OVF Template wizard step 3 with mgmt-cluster-01 selected and compatibility checks succeeded." width="1143" height="601" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Review the deployment details — note the 914 GB thick footprint vs 4.2 GB thin&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/006-capture-006.webp" alt="Review details step of the Deploy OVF Template wizard showing VMware VCF SDDC Manager Appliance version 9.1.0.0400." width="1141" height="609" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read and accept the license agreements&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/007-capture-007.webp" alt="License agreements step of the Deploy OVF Template wizard with the Broadcom Foundation Agreement accepted." width="1144" height="600" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select a datastore for the deployment&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/008-capture-008.webp" alt="Select storage step of the Deploy OVF Template wizard with the vcf-vsan datastore selected and compatibility checks succeeded." width="1149" height="608" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the destination network — the appliance needs to reach the ESX hosts, the VM management network, DNS, and NTP&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/009-capture-009.webp" alt="Select networks step of the Deploy OVF Template wizard mapping Network 1 to the sddc-vds01-mgmt port group with static IPv4 allocation." width="1143" height="605" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fill in the template customization — root and local user passwords (minimum 15 characters with the usual complexity rules; the local password is what you will use for &lt;strong&gt;admin@local&lt;/strong&gt; in the UI and &lt;strong&gt;vcf&lt;/strong&gt; over SSH), hostname, networking, DNS, and NTP&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is also where 9.1 differs from 9.0: the networking section now starts with an &lt;strong&gt;IP Address Version&lt;/strong&gt; selector, with IPv6 address, prefix length, and gateway fields if you go dual-stack.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/010-capture-010.webp" alt="Customize template step of the Deploy OVF Template wizard with masked root and local user password fields for the VCF Installer appliance." width="1142" height="610" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Review the summary&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Finish&lt;/strong&gt; and wait for the deployment to complete&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/011-capture-011.webp" alt="Ready to complete summary of the Deploy OVF Template wizard for the VCF-SDDC-Manager-Appliance-9.1.0.0400.25570100 OVA." width="1146" height="608" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="log-in-and-connect-the-depot"&gt;Log in and connect the depot&lt;/h2&gt;
&lt;p&gt;Once the appliance is up, navigate to https://ip_address/ui or https://hostname/ui. Same as 9.0, the services take a few minutes to start after first boot.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For username use &lt;strong&gt;admin@local&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Enter the local user password from the OVA deployment&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Log in&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/012-capture-012.webp" alt="VMware Cloud Foundation Installer login page with admin@local entered as the username." width="577" height="668" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Get Started page opens with the Download Binaries card flagging &lt;strong&gt;No Depot Connection&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Depot Settings and Binary Management&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/013-capture-013.webp" alt="The VCF Installer Get Started page with the Download Binaries card showing a No Depot Connection warning." width="586" height="327" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Both depot types are available: online (direct or via proxy) and offline, fed by the VCF Download Tool — only one can be connected at a time. For air-gapped environments the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/preparing-your-environment/downloading-binaries-to-the-vcf-installer-appliance/download-install-binaries-to-an-offline-depot.html"&gt;official offline depot procedure&lt;/a&gt; covers the Download Tool flow; William Lam also documented the new &lt;a href="https://williamlam.com/2026/05/vcf-9-1-new-http-offline-depot-support-for-vcf-installer-fleet-depot-service.html"&gt;HTTP offline depot support in 9.1&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Configure&lt;/strong&gt; under the &lt;strong&gt;Online Depot&lt;/strong&gt; tile&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/014-capture-014.webp" alt="Depot settings and binary management page with unconfigured Online Depot and Offline Depot tiles." width="1465" height="650" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Here is the big change from 9.0: instead of pasting a download token, the dialog shows the appliance&amp;rsquo;s &lt;strong&gt;Download Service ID&lt;/strong&gt; (the docs and KB call it the Software Depot ID). Take that ID to the BSC portal at &lt;a href="https://vcf.broadcom.com"&gt;vcf.broadcom.com&lt;/a&gt;, register it, and you get an &lt;strong&gt;Activation Code&lt;/strong&gt; back — the full flow is in the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/preparing-your-environment/downloading-binaries-to-the-vcf-installer-appliance/connect-to-an-online-depot-to-download-binaries.html"&gt;official depot procedure&lt;/a&gt; and &lt;a href="https://knowledge.broadcom.com/external/article/443647/download-token-has-been-replaced-by-acti.html"&gt;KB 443647&lt;/a&gt;. The download token from &lt;a href="https://knowledge.broadcom.com/external/article/390098"&gt;KB 390098&lt;/a&gt; that we used in the 9.0 post is not used here anymore.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Copy the &lt;strong&gt;Download Service ID&lt;/strong&gt; and register it in the BSC portal to obtain the Activation Code&lt;/li&gt;
&lt;li&gt;Enter the &lt;strong&gt;Activation Code&lt;/strong&gt; (enable the proxy toggle if you need one)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Authenticate&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/015-capture-015.webp" alt="The Online Depot dialog showing the Download Service ID with the Activation Code field to be filled from the BSC portal." width="853" height="402" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="download-the-binaries"&gt;Download the binaries&lt;/h2&gt;
&lt;p&gt;With the depot connected, the Binary Management section lists everything available for the release.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Confirm the Online Depot tile shows &lt;strong&gt;Depot connection active&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;Binary Management&lt;/strong&gt;, select the product and version — &lt;strong&gt;VMware Cloud Foundation 9.1.0.0&lt;/strong&gt; in my case&lt;/li&gt;
&lt;li&gt;Select the binaries and click &lt;strong&gt;Download&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/016-capture-016.webp" alt="Depot settings page with the online depot connection active and the VMware Cloud Foundation 9.1.0.0 binaries listed as not downloaded." width="1434" height="614" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The component list is a good illustration of how 9.1 patch waves work: the installer pulls the latest patch of each component rather than a single uniform build — SDDC Manager 9.1.0.0400, NSX 9.1.0.0200, VCF Automation 9.1.0.0200, VCF Operations 9.1.0.0400, and so on. William Lam has a good write-up of &lt;a href="https://williamlam.com/2026/06/vcf-9-1-quick-tip-understanding-vcf-installer-default-behavior-for-vcf-patch-releases.html"&gt;this default behavior&lt;/a&gt; if you need to pin earlier versions. Also note the sizes — VCF services runtime alone is 17.42 GB and VCF Automation 14.95 GB, so give the downloads some time.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/017-capture-017.webp" alt="Binary Management table with all VMware Cloud Foundation 9.1 components showing download status Success." width="1406" height="582" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="start-the-deployment-wizard"&gt;Start the Deployment Wizard&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Return Home&lt;/strong&gt; — the Deploy card sits next to Download Binaries. Progress in the wizard is saved locally in the browser after each step, so you can safely step away&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/018-capture-018.webp" alt="The VCF Installer home page with the Download Binaries and Deploy cards side by side." width="864" height="252" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Deployment Wizard&lt;/strong&gt; and select &lt;strong&gt;VMware Cloud Foundation&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The dropdown also offers &lt;strong&gt;VMware vSphere Foundation&lt;/strong&gt; — the 9.1 installer deploys VVF through the same wizard framework, with a reduced flow (no NSX Manager or SDDC Manager steps). That is a post for another day; everything below follows the VCF path.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/019-capture-019.webp" alt="The Deploy card with the Deployment Wizard dropdown expanded showing VMware Cloud Foundation and VMware vSphere Foundation options." width="425" height="296" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="deploy-vmware-cloud-foundation"&gt;Deploy VMware Cloud Foundation&lt;/h2&gt;
&lt;p&gt;The wizard runs through four phases — Introduction, Plan, Prepare, Deploy; the official procedure is &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/deploy-a-new-vcf-fleet-or-a-new-vcf-instance.html"&gt;Start a New VCF Fleet or a New VCF Instance Deployment&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;The About page is worth an actual read: it diagrams the VCF fleet concept and calls out that &lt;strong&gt;VCF 9.1 introduces VCF management services&lt;/strong&gt; inside the management domain, next to vCenter, NSX Manager, and SDDC Manager. The installer deploys the management domain; workload domains come later from VCF Operations.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/032-capture-032.webp" alt="The VCF Installer deployment wizard introduction page showing the VCF fleet diagram with VCF Operations, VCF Automation, and the first VCF instance’s management domain." width="1496" height="658" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Three deployment paths are offered: a new VCF fleet, a new VCF instance joining an existing fleet&amp;rsquo;s VCF Operations, or deploying deferred components. Same as in my 9.0 fleet post, this lab is a fresh start.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Deploy a new VCF fleet&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/033-capture-033.webp" alt="The Deployment Paths step with Deploy a new VCF fleet selected over the new VCF Instance and deferred components options." width="1478" height="386" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="plan"&gt;Plan&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Check &lt;strong&gt;I have an existing vCenter instance&lt;/strong&gt; — the wizard will collect and validate the details later&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can also flag an existing VCF Operations 9.1 instance or an NSX-registered vCenter here. VCF Automation is no longer a hard requirement at deployment time — it can be connected or deployed later as a day-N operation.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/034-capture-034.webp" alt="The Plan phase Existing Component step with the existing vCenter checkbox selected." width="1494" height="701" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the &lt;strong&gt;Deployment model&lt;/strong&gt; — I went with &lt;strong&gt;Simple&lt;/strong&gt;, which locks the size to &lt;strong&gt;Small&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Review the component sizing table and click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The sizing table is part of the new integrated planning workflow, and it is worth pausing on: VCF management services alone ask for 40 vCPUs, 82 GB RAM, and 3 TB of disk in this smallest configuration.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/035-capture-035.webp" alt="The Size Options step with the Simple deployment model and Small size selected, showing per-component vCPU, RAM, and disk requirements." width="1492" height="659" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Review the &lt;strong&gt;Network Options&lt;/strong&gt; — by default VCF Operations, VCF Automation, and the management services land on the VM management network, and a distributed Transit Gateway is configured to make the domain VPC-ready&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt; (or &lt;strong&gt;Customize&lt;/strong&gt; if you need separate networks)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One caveat straight from the wizard: with an existing vCenter the wizard supports IPv4-only; dual-stack deployments have to go through a JSON spec.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/036-capture-036.webp" alt="The Network Options step showing the default network configuration with the Customize button." width="1488" height="661" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Review Prerequisites step is the payoff of the planning workflow: capacity totals (2 hosts, 84 vCPUs, 254 GB RAM, 5,244 GB disk for my configuration), VLAN requirements (none for this path), and the exact FQDN and IP requirements — 12 IPs for the management services pool, 5 for VCF Automation, plus FQDNs for Cloud proxy, NSX Manager, VCF Operations, and the License Server. The &lt;strong&gt;Pre-fill generated FQDNs in wizard&lt;/strong&gt; button carries these into the later steps. Remember: lowercase only, forward and reverse resolvable.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the DNS records and reserve the IP ranges&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/037-capture-037.webp" alt="The Review Prerequisites step listing capacity totals, VLAN requirements, and the FQDNs and IP address pools needed for the deployment." width="1487" height="656" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="prepare"&gt;Prepare&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Select the &lt;strong&gt;Version&lt;/strong&gt; — 9.1.0.0, with the latest downloaded patch of each component applied automatically&lt;/li&gt;
&lt;li&gt;Name the VCF instance and the management domain&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/038-capture-038.webp" alt="The Prepare phase General Information step with version 9.1.0.0, instance name instance-01, and management domain name mgmt." width="1493" height="660" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The VCF Management step collects the FQDNs for VCF Operations, the License Server, and the fleet, instance, and services-runtime components — this is where the pre-filled FQDNs from the Plan phase save the typing.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enter the details of the existing vCenter — FQDN, administrator, and root credentials&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Connect&lt;/strong&gt; — the cluster running the vCenter appliance is selected as the deployment destination&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/039-capture-039.webp" alt="The Existing vCenter step with fields for the vCenter FQDN, administrator credentials, and root password and the Connect button." width="1492" height="656" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Provide the IP pools — a single IPv4 range works for both: minimum 12 addresses for the VCF Management Services pool and 5 for the VCF Automation pool&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/040-capture-040.webp" alt="The Networks step with the VCF Management Services and VCF Automation IP pool ranges to be filled in." width="1492" height="650" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enter the NSX Manager cluster and appliance FQDNs&lt;/li&gt;
&lt;li&gt;Decide on the overlay networking mode&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;strong&gt;Configure overlay using ESX Management VMkernel Networking&lt;/strong&gt; toggle is one of my favorite quality-of-life items in 9.x: NSX 9 can run overlay over the ESX management VMkernel network without a VLAN-backed transport configuration. Unchecked, NSX is configured in VLAN transport zone mode instead.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/041-capture-041.webp" alt="The NSX Manager step with cluster and appliance FQDN fields and the overlay via ESX Management VMkernel Networking toggle enabled." width="1496" height="663" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enter the SDDC Manager FQDN and the installer appliance password&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The info banner explains why the appliance password is requested: when the installer appliance runs on one of the management domain hosts, it is converted into the SDDC Manager appliance during deployment — the same appliance transition the official docs describe, and the reason the prerequisites page noted the SDDC Manager FQDN is optional in that placement.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/042-capture-042.webp" alt="The SDDC Manager step with the FQDN filled in and the info banner explaining the installer appliance is converted into SDDC Manager during deployment." width="1498" height="658" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="deploy"&gt;Deploy&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Review the summary — every section from the wizard is expandable here&lt;/li&gt;
&lt;li&gt;Optionally click &lt;strong&gt;Download JSON Spec&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The JSON preview tab and the downloadable spec make the whole deployment repeatable — the same file drives API-based deployments, and it is the vehicle for the options the wizard does not expose, like dual-stack networking or an alternative internal cluster CIDR.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/043-capture-043.webp" alt="The Review step of the Deploy phase showing the deployment summary with the Download JSON Spec option." width="1495" height="659" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Wait for the 11 validation checks to complete — deployment specification, security configuration, DNS resolution, versions and bundles, existing SDDC Manager configuration, password policies, network configuration, existing components, and the capacity calculations&lt;/li&gt;
&lt;li&gt;Resolve any errors, acknowledge warnings if appropriate, and click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-91-installer/044-capture-044.webp" alt="The Validate and Deploy step with validation in progress showing 0 of 11 checks completed and the Deploy button still disabled." width="1493" height="654" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;From here the installer takes over: management services, VCF Operations, NSX, SDDC Manager conversion, and the rest of the management domain roll out in sequence. Depending on hardware, expect this to run for a few hours.&lt;/p&gt;
&lt;h2 id="if-validation-or-deployment-fails"&gt;If validation or deployment fails&lt;/h2&gt;
&lt;p&gt;A few 9.1-specific failure modes worth knowing before you hit Deploy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;Deploy and configure VCF Management Platform&amp;rdquo; fails&lt;/strong&gt; — the management services use an internal cluster CIDR of 198.18.0.0/15 by default; if that overlaps your network, &lt;a href="https://knowledge.broadcom.com/external/article/440541/deploying-vcf-91-fails-at-deploy-and-con.html"&gt;KB 440541&lt;/a&gt; covers switching it to 240.0.0.0/15 or 250.0.0.0/15 via the JSON spec.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;VCF Management Services Deployment Task&amp;rdquo; fails on NTP&lt;/strong&gt; — the NTP server must be reachable over UDP 123 from the deployed appliances, not just from the installer: &lt;a href="https://knowledge.broadcom.com/external/article/439240/vcf-91-installer-deployment-is-failing-i.html"&gt;KB 439240&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vMotion / vSAN network connectivity validations fail&lt;/strong&gt; — the installer does not recognize standby uplinks; set the vmnics active/active or disable the check per &lt;a href="https://knowledge.broadcom.com/external/article/437248/91-vcf-installer-fails-on-vmotion-networ.html"&gt;KB 437248&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uppercase FQDNs&lt;/strong&gt; — deployment fails for fleet components, identity broker, services runtime, and VCF Automation; lowercase everything (&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/known-issues/vcf-installer-91-known-issues.html"&gt;known issues&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For nested labs, William Lam maintains a &lt;a href="https://williamlam.com/2026/05/vcf-9-1-comprehensive-vcf-installer-sddc-manager-configuration-workarounds-for-lab-deployments.html"&gt;comprehensive set of installer workarounds for 9.1&lt;/a&gt; — pNIC speed, vSAN HCL, and MTU prechecks included. And a neat trick from the same source: the installer doubles as a &lt;a href="https://williamlam.com/2026/05/vcf-9-1-quick-tip-using-the-vcf-installer-as-a-standalone-pre-check-tool.html"&gt;standalone pre-check tool&lt;/a&gt; — you can run the full validation suite against your infrastructure without deploying anything.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Official documentation&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-.html"&gt;Deploying a New VCF or vSphere Foundation Platform (9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/preparing-your-environment/deploy-the-vmware-cloud-foundation-installer-appliance.html"&gt;Deploy VCF Installer (9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/preparing-your-environment/downloading-binaries-to-the-vcf-installer-appliance.html"&gt;Downloading Binaries to VCF Installer (9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/preparing-your-environment/downloading-binaries-to-the-vcf-installer-appliance/connect-to-an-online-depot-to-download-binaries.html"&gt;Connect VCF Installer to Broadcom or an Offline Depot (9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/deployment/deploying-a-new-vmware-cloud-foundation-or-vmware-vsphere-foundation-private-cloud-/deploy-a-new-vcf-fleet-or-a-new-vcf-instance.html"&gt;Start a New VCF Fleet or a New VCF Instance Deployment (9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/planning-and-preparation/vcf-components-fqdns-and-ip-addresses/first-vcf-instance-fqdns-and-ip-addresses.html"&gt;First VCF Instance FQDNs and IP Addresses (9.1 planning)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes.html"&gt;VCF 9.1 Release Notes&lt;/a&gt; · &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/vmware-cloud-foundation-bill-of-materials.html"&gt;Bill of Materials&lt;/a&gt; · &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/what-s-new/whats-new-installer.html"&gt;What&amp;rsquo;s New — VCF Installer&lt;/a&gt; · &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/known-issues/vcf-installer-91-known-issues.html"&gt;VCF Installer Known Issues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Broadcom KBs&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/443647/download-token-has-been-replaced-by-acti.html"&gt;KB 443647 — Download token replaced by Activation Code in VCF Installer 9.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/401497/vmware-vcf-90-or-vvf-90-downloads-in-the.html"&gt;KB 401497 — VCF/VVF downloads in the Broadcom Support Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/440541/deploying-vcf-91-fails-at-deploy-and-con.html"&gt;KB 440541 — VCF 9.1 fails at Deploy and configure VCF Management Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/439240/vcf-91-installer-deployment-is-failing-i.html"&gt;KB 439240 — VCF 9.1 installer fails at VCF Management Services Deployment Task&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/437248/91-vcf-installer-fails-on-vmotion-networ.html"&gt;KB 437248 — VCF 9.1 installer fails on vMotion/vSAN network connectivity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;My VCF 9.0 installer series&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cosmin.us/deploying-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;Deploying the VMware Cloud Foundation Installer Appliance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cosmin.us/configuring-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;Configuring the VMware Cloud Foundation Installer Appliance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cosmin.us/deploy-a-vcf-9-instance-in-an-existing-vcf-fleet/"&gt;Deploy a VCF 9 Instance in a New VCF Fleet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Upgrading VCF 9.1 Management Services to 9.1.0.0400 Step by Step</title><link>https://cosmin.us/upgrading-vcf-9-1-management-services-to-9-1-0-0400-step-by-step/</link><pubDate>Mon, 03 Aug 2026 10:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/upgrading-vcf-9-1-management-services-to-9-1-0-0400-step-by-step/</guid><description>&lt;p&gt;On July 13, 2026 Broadcom shipped the next VCF 9.1 patch wave, and this post walks through the part of it that lives under &lt;strong&gt;Build -&amp;gt; Lifecycle -&amp;gt; VCF Management -&amp;gt; Upgrade&lt;/strong&gt;: the VCF Management services. The headline releases on this surface are &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-operations/9-1-0-0400/vcfoperations-9-1-0-0400-release-notes.html"&gt;VCF Operations 9.1.0.0400&lt;/a&gt; and &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-automation/vcfautomation-9-1-0-0200-release-notes.html"&gt;VCF Automation 9.1.0.0200&lt;/a&gt;, mostly security enhancements plus a batch of fixes. This post is the follow-up to my &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;9.1.0.0300 walkthrough&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To be clear on scope: this post covers only the management services. SDDC Manager 9.1.0.0400 and the core components — vCenter, NSX, ESX, vSAN — go through the VCF Instances flow and are not covered here; see the short section at the end.&lt;/p&gt;</description><content:encoded>&lt;p&gt;On July 13, 2026 Broadcom shipped the next VCF 9.1 patch wave, and this post walks through the part of it that lives under &lt;strong&gt;Build -&amp;gt; Lifecycle -&amp;gt; VCF Management -&amp;gt; Upgrade&lt;/strong&gt;: the VCF Management services. The headline releases on this surface are &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-operations/9-1-0-0400/vcfoperations-9-1-0-0400-release-notes.html"&gt;VCF Operations 9.1.0.0400&lt;/a&gt; and &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-automation/vcfautomation-9-1-0-0200-release-notes.html"&gt;VCF Automation 9.1.0.0200&lt;/a&gt;, mostly security enhancements plus a batch of fixes. This post is the follow-up to my &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;9.1.0.0300 walkthrough&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To be clear on scope: this post covers only the management services. SDDC Manager 9.1.0.0400 and the core components — vCenter, NSX, ESX, vSAN — go through the VCF Instances flow and are not covered here; see the short section at the end.&lt;/p&gt;
&lt;p&gt;One thing to understand up front: &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x.html"&gt;patch releases are not synchronized among all VCF components&lt;/a&gt;, and you can pick and choose which components to apply. That is why my target list below is a mix of 0400, 0200, and 0100 builds. It looks odd the first time. It is expected.&lt;/p&gt;
&lt;h2 id="versions-in-this-update"&gt;Versions in This Update&lt;/h2&gt;
&lt;p&gt;Here is the full From/To picture from my lab, exactly as the VCF Operations UI reports it.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Component&lt;/th&gt;
					&lt;th&gt;Current Version&lt;/th&gt;
					&lt;th&gt;Target Version&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Fleet Lifecycle&lt;/td&gt;
					&lt;td&gt;9.1.0.0300.25536195&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25570104&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF Operations&lt;/td&gt;
					&lt;td&gt;9.1.0.0300.25531073&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25541561&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF Operations for Networks&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25427421&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25517220&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Identity Broker&lt;/td&gt;
					&lt;td&gt;9.1.0.0.25368698&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25522734&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF Automation&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25429499&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25556825&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Log Management&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25435121&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25544947&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SDDC Lifecycle&lt;/td&gt;
					&lt;td&gt;9.1.0.0300.25536194&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25570103&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Real-Time Metrics&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25435036&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25544944&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Real-Time Metrics Store&lt;/td&gt;
					&lt;td&gt;9.1.0.0.25370367&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25555874&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Salt Master&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25434834&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25544946&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Salt RaaS&lt;/td&gt;
					&lt;td&gt;9.1.0.0100.25434834&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25544946&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Software Depot&lt;/td&gt;
					&lt;td&gt;9.1.0.0.25371105&lt;/td&gt;
					&lt;td&gt;9.1.0.0400.25570105&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Migration Service Engine&lt;/td&gt;
					&lt;td&gt;9.1.0.0.25370929&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25556825&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF Services Runtime&lt;/td&gt;
					&lt;td&gt;9.1.0.0.25370367&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25555874&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;SDDC Manager &lt;strong&gt;9.1.0.0400&lt;/strong&gt; (build &lt;strong&gt;25570100&lt;/strong&gt;) ships in the same wave but lives outside this component list — see the scope section at the end of the post.&lt;/p&gt;
&lt;h2 id="before-you-start"&gt;Before You Start&lt;/h2&gt;
&lt;p&gt;A few rules before clicking anything. &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/lifecycle-management/using-the-depot-configuration-tab/upgrade-a-vcf-management-component.html"&gt;Back up your components before patching, and remember only one upgrade or patch operation can run at a time&lt;/a&gt;. That single-operation rule matters more than it sounds with fourteen components in the queue — plan for a long session or spread it over a maintenance window. If your depot is online, the binaries pull directly. If you run offline, update the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vmware-download-tool/vcf-download-tool-9-1-0-0400-release-notes.html"&gt;VCF Download Tool to &lt;strong&gt;9.1.0.0400&lt;/strong&gt;&lt;/a&gt; (build &lt;strong&gt;25550178&lt;/strong&gt;) first and download the binaries with it.&lt;/p&gt;
&lt;p&gt;Two known issues shape how I ran this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-operations/9-1-0-0400/sddc-lifecycle-9-1-0-0400-release-notes.html"&gt;SDDC Lifecycle 9.1.0.0400&lt;/a&gt;: the batch precheck can fail with &lt;strong&gt;VCF_LCM_FLEET_UPGRADE_400_COMPONENT_NOT_NEEDED_USER_INPUT_SPEC_PROVIDED&lt;/strong&gt;. Do not use Run Prechecks (ALL). Precheck each component individually.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-operations/9-1-0-0400/software-depot-9-1-0-0400-release-notes.html"&gt;Software Depot 9.1.0.0400&lt;/a&gt;: patching the depot takes it temporarily offline. Do not run any other component patch at the same time, and retry anything that failed afterwards.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="upgrade-fleet-lifecycle"&gt;Upgrade Fleet Lifecycle&lt;/h2&gt;
&lt;p&gt;Broadcom&amp;rsquo;s ordering guidance for maintenance releases starts with Fleet Lifecycle. For patch releases the order is flexible, but I still go Fleet Lifecycle first — it drives patch deployment for the rest of the fleet components.&lt;/p&gt;
&lt;p&gt;In VCF Operations, go to &lt;strong&gt;Build -&amp;gt; Lifecycle -&amp;gt; VCF Management -&amp;gt; Upgrade&lt;/strong&gt;. Open the &amp;ldquo;&amp;hellip;&amp;rdquo; menu on the Fleet Lifecycle card and click Select version.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/001-capture-001.webp" alt="The VCF Management Upgrade page with the Fleet Lifecycle three-dot menu expanded and the Select version option visible." width="1282" height="356" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Fleet Lifecycle Component Target Version dialog opens. Set the target to &lt;strong&gt;9.1.0.0400.25570104&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/002-capture-002.webp" alt="The Fleet Lifecycle Component Target Version dialog with 9.1.0.0400.25570104 selected." width="858" height="255" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Back on the card, the upgrade path now reads &lt;strong&gt;9.1.0.0300.25536195&lt;/strong&gt; to &lt;strong&gt;9.1.0.0400.25570104&lt;/strong&gt;. Click Upgrade.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/003-capture-003.webp" alt="The Fleet Lifecycle card showing the upgrade path to 9.1.0.0400.25570104 with the Upgrade button." width="915" height="127" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Kick off the upgrade. The card flips to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/004-capture-004.webp" alt="The Fleet Lifecycle card showing Upgrade in progress with a link to Upgrade details." width="963" height="124" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Open Upgrade details. The first two subtasks are &amp;ldquo;Set Fleet lifecycle upgrade context&amp;rdquo; and &amp;ldquo;Stage Fleet lifecycle plugin in VCF services runtime&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/005-capture-005.webp" alt="The Fleet Lifecycle upgrade workflow with its first two subtasks, Set Fleet lifecycle upgrade context and Stage Fleet lifecycle plugin in VCF services runtime." width="1308" height="511" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The status messages move through Staging binaries and Running Component Stage Prechecks. Nothing to do here but watch.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/006-capture-006.webp" alt="The Fleet Lifecycle upgrade workflow showing the Staging binaries and Running Component Stage Prechecks messages." width="1303" height="729" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;All 7 subtasks land on Completed. In this run the whole thing took about 45 minutes, 11:08 to 11:54 AM.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/007-capture-007.webp" alt="The Fleet Lifecycle upgrade workflow with all 7 subtasks showing Completed after roughly 45 minutes." width="1315" height="680" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="set-the-vcf-management-target-version"&gt;Set the VCF Management Target Version&lt;/h2&gt;
&lt;p&gt;With Fleet Lifecycle done, click CHANGE TARGET VERSION.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/008-capture-008.webp" alt="The VCF Management page with the CHANGE TARGET VERSION button highlighted after the Fleet Lifecycle upgrade." width="470" height="61" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Set Target Version dialog offers the VCF global version 9.1.0.*.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/009-capture-009.webp" alt="The Set Target Version dialog showing the VCF global version 9.1.0.* selection." width="850" height="279" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The component list now shows everything Ready for upgrade — with the mixed 0400/0200/0100 targets from the table above. This is the per-component patch numbering at work, not a depot problem.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/010-capture-010.webp" alt="The VCF Management component list showing every component Ready for upgrade with mixed 9.1.0.0400, 9.1.0.0200, and 9.1.0.0100 target versions." width="1265" height="641" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Because of the SDDC Lifecycle known issue, I did not touch RUN PRECHECKS (ALL). I ran prechecks per component instead, right before each upgrade. It costs a few extra clicks per component, but it avoids the &lt;strong&gt;VCF_LCM_FLEET_UPGRADE_400_COMPONENT_NOT_NEEDED_USER_INPUT_SPEC_PROVIDED&lt;/strong&gt; failure entirely.&lt;/p&gt;
&lt;h2 id="upgrade-vcf-operations"&gt;Upgrade VCF Operations&lt;/h2&gt;
&lt;p&gt;First up, VCF Operations itself to &lt;strong&gt;9.1.0.0400.25541561&lt;/strong&gt;. Note that the cloud proxy and the license server are patched automatically as part of this one — no separate steps needed for either.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/011-capture-011.webp" alt="The VCF Operations row Ready for upgrade with target version 9.1.0.0400.25541561." width="1254" height="312" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The workflow runs through its subtasks; I moved on once it was In Progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/012-capture-012.webp" alt="The VCF Operations upgrade workflow with its subtasks In Progress." width="1305" height="723" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-identity-broker"&gt;Upgrade Identity Broker&lt;/h2&gt;
&lt;p&gt;Next, Identity Broker to &lt;strong&gt;9.1.0.0100.25522734&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/013-capture-013.webp" alt="The Identity Broker row Ready for upgrade with target version 9.1.0.0100.25522734." width="1159" height="87" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;This one is a 6-subtask workflow.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/014-capture-014.webp" alt="The Identity Broker upgrade workflow showing its 6 subtasks In Progress." width="1315" height="614" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-vcf-automation"&gt;Upgrade VCF Automation&lt;/h2&gt;
&lt;p&gt;VCF Automation goes to &lt;strong&gt;9.1.0.0200.25556825&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/015-capture-015.webp" alt="The VCF Automation row Ready for upgrade with target version 9.1.0.0200.25556825." width="1209" height="168" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The precheck workflow runs first, then the upgrade takes over.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/016-capture-016.webp" alt="The VCF Automation precheck workflow running before the upgrade proceeds." width="1313" height="704" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-vcf-operations-for-networks"&gt;Upgrade VCF Operations for Networks&lt;/h2&gt;
&lt;p&gt;VCF Operations for Networks moves to &lt;strong&gt;9.1.0.0200.25517220&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/017-capture-017.webp" alt="The VCF Operations for Networks row Ready for upgrade with target version 9.1.0.0200.25517220." width="1246" height="172" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-log-management"&gt;Upgrade Log Management&lt;/h2&gt;
&lt;p&gt;Log Management targets &lt;strong&gt;9.1.0.0400.25544947&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/019-capture-019.webp" alt="The Log Management row Ready for upgrade with target version 9.1.0.0400.25544947." width="1056" height="100" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Same pattern: precheck, then the upgrade workflow.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/020-capture-020.webp" alt="The Log Management upgrade workflow with its subtasks In Progress." width="1068" height="710" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-sddc-lifecycle"&gt;Upgrade SDDC Lifecycle&lt;/h2&gt;
&lt;p&gt;SDDC Lifecycle goes to &lt;strong&gt;9.1.0.0400.25570103&lt;/strong&gt; — the component behind the batch precheck known issue, upgraded here with its own individual precheck.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/021-capture-021.webp" alt="The SDDC Lifecycle row Ready for upgrade with target version 9.1.0.0400.25570103." width="1054" height="92" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-real-time-metrics-and-metrics-store"&gt;Upgrade Real-Time Metrics and Metrics Store&lt;/h2&gt;
&lt;p&gt;Real-Time Metrics targets &lt;strong&gt;9.1.0.0400.25544944&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/022-capture-022.webp" alt="The Real-Time Metrics row Ready for upgrade with target version 9.1.0.0400.25544944." width="1054" height="90" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Real-Time Metrics row switches to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/023-capture-023.webp" alt="The Real-Time Metrics row showing Upgrade in progress with the Upgrade details link." width="855" height="66" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Then the upgrade workflow itself.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/024-capture-024.webp" alt="The Real-Time Metrics upgrade workflow with its subtasks In Progress." width="1065" height="224" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Real-Time Metrics Store is a separate component; it targets &lt;strong&gt;9.1.0.0200.25555874&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/025-capture-025.webp" alt="The Real-Time Metrics Store row Ready for upgrade with target version 9.1.0.0200.25555874." width="1051" height="92" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Real-Time Metrics Store row switches to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/026-capture-026.webp" alt="The Real-Time Metrics Store row showing Upgrade in progress with the Upgrade details link." width="857" height="56" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;And the upgrade workflow follows.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/027-capture-027.webp" alt="The Real-Time Metrics Store upgrade workflow with its subtasks In Progress." width="1064" height="412" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-salt-master"&gt;Upgrade Salt Master&lt;/h2&gt;
&lt;p&gt;Salt Master targets &lt;strong&gt;9.1.0.0400.25544946&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/029-capture-029.webp" alt="The Salt Master row Ready for upgrade with target version 9.1.0.0400.25544946." width="1051" height="95" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Salt Master row switches to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/030-capture-030.webp" alt="The Salt Master row showing Upgrade in progress with the Upgrade details link." width="847" height="58" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The upgrade workflow takes over and works through its subtasks.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/031-capture-031.webp" alt="The Salt Master upgrade workflow with its subtasks In Progress." width="1067" height="584" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-software-depot"&gt;Upgrade Software Depot&lt;/h2&gt;
&lt;p&gt;Software Depot goes to &lt;strong&gt;9.1.0.0400.25570105&lt;/strong&gt;. Per the known issue in the release notes, the depot goes briefly offline during its own patch, so avoid launching other component patches at the same time and retry anything that failed afterwards.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/033-capture-033.webp" alt="The Software Depot row Ready for upgrade with target version 9.1.0.0400.25570105." width="1056" height="95" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Software Depot row switches to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/034-capture-034.webp" alt="The Software Depot row showing Upgrade in progress with the Upgrade details link." width="854" height="59" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Then the upgrade workflow, while the depot is briefly unavailable.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/035-capture-035.webp" alt="The Software Depot upgrade workflow with its subtasks In Progress while the depot is temporarily offline." width="1064" height="662" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-migration-service-engine"&gt;Upgrade Migration Service Engine&lt;/h2&gt;
&lt;p&gt;Migration Service Engine targets &lt;strong&gt;9.1.0.0200.25556825&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/036-capture-036.webp" alt="The Migration Service Engine row Ready for upgrade with target version 9.1.0.0200.25556825." width="1053" height="93" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The Migration Service Engine row switches to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/037-capture-037.webp" alt="The Migration Service Engine row showing Upgrade in progress with the Upgrade details link." width="849" height="56" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Fun detail in the workflow panel: the staging task exposes the internal component name &lt;strong&gt;VCD_MIGRATOR&lt;/strong&gt; — &amp;ldquo;Stage VCD_MIGRATOR plugin in VCF services runtime&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/038-capture-038.webp" alt="The Migration Service Engine upgrade workflow with the Stage VCD_MIGRATOR plugin in VCF services runtime subtask." width="1069" height="386" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-vcf-services-runtime"&gt;Upgrade VCF Services Runtime&lt;/h2&gt;
&lt;p&gt;VCF Services Runtime goes to &lt;strong&gt;9.1.0.0200.25555874&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/039-capture-039.webp" alt="The VCF Services Runtime row Ready for upgrade with target version 9.1.0.0200.25555874." width="1051" height="95" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The VCF Services Runtime row switches to Upgrade in progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/040-capture-040.webp" alt="The VCF Services Runtime row showing Upgrade in progress with the Upgrade details link." width="851" height="56" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Then the upgrade workflow with its subtasks.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/041-capture-041.webp" alt="The VCF Services Runtime upgrade workflow with its subtasks In Progress." width="1057" height="404" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-salt-raas"&gt;Upgrade Salt RaaS&lt;/h2&gt;
&lt;p&gt;Salt RaaS ran last in my lab, targeting &lt;strong&gt;9.1.0.0400.25544946&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/028-capture-028.webp" alt="The Salt RaaS row Ready for upgrade with target version 9.1.0.0400.25544946." width="1053" height="95" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;At the time of this capture the precheck workflow was still running — &amp;ldquo;Run Salt RaaS upgrade prechecks&amp;rdquo; shows In Progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/08/vcf-9-1-0400-upgrade/044-capture-044.webp" alt="The Salt RaaS precheck workflow with the Run Salt RaaS upgrade prechecks subtask still In Progress." width="1060" height="520" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="what-about-sddc-manager-and-the-core-components"&gt;What About SDDC Manager and the Core Components?&lt;/h2&gt;
&lt;p&gt;They are out of scope for this post. &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-installer/sddc-manager-9-1-0-0400-release-notes.html"&gt;SDDC Manager 9.1.0.0400&lt;/a&gt; (build &lt;strong&gt;25570100&lt;/strong&gt;) and the core components from this wave — ESX 9.1.0.0200, vSAN 9.1.0.0200, NSX 9.1.0.0200, and vCenter 9.1.0.0200, later bumped to 9.1.0.0300 on July 29, 2026 — are applied through &lt;strong&gt;Build -&amp;gt; Lifecycle -&amp;gt; VCF Instances&lt;/strong&gt;. That is the same flow I documented in &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;Upgrading VCF 9.1 to 9.1.0.0300 Step by Step&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="validate-the-update"&gt;Validate the Update&lt;/h2&gt;
&lt;p&gt;Before calling it done:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the Component Versions page — every patched management component should show On Target.&lt;/li&gt;
&lt;li&gt;Check the task list — no failed workflows left behind.&lt;/li&gt;
&lt;li&gt;Confirm the VCF Operations UI is reachable and healthy.&lt;/li&gt;
&lt;li&gt;Retry anything that failed while the Software Depot was being patched — the depot was offline during that window.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For SDDC Manager and the core components, see the previous section — the flow in &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;Upgrading VCF 9.1 to 9.1.0.0300 Step by Step&lt;/a&gt; still applies one-to-one.&lt;/p&gt;</content:encoded></item><item><title>Fixing the Swagger localhost:8600 Server URL in VCF Automation 9.1: Overriding a Spec Baked Into the Image</title><link>https://cosmin.us/fixing-swagger-localhost-8600-server-url-vcf-automation-9-1/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/fixing-swagger-localhost-8600-server-url-vcf-automation-9-1/</guid><description>&lt;p&gt;While putting together the &lt;a href="https://cosmin.us/vcf-automation-9-1-api-getting-started/"&gt;VCF Automation 9.1 API getting started&lt;/a&gt; post, I spent some time in the in-product API Help Center — log into the VM Apps tenant, click your username, &lt;strong&gt;API Help Center &amp;gt; Explore Automation APIs&lt;/strong&gt;. The Swagger pages render fine, but the moment you try an actual call, it fails. The &lt;strong&gt;Servers&lt;/strong&gt; box for the VCF Automation (Blueprint) API shows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://localhost:8600
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Swagger UI builds Try It Out requests from &lt;code&gt;servers[0].url&lt;/code&gt;, so the browser dutifully sends every request to port 8600 on &lt;em&gt;your own workstation&lt;/em&gt;. Connection refused, every time. The documentation is correct; the advertised host is not.&lt;/p&gt;</description><content:encoded>&lt;p&gt;While putting together the &lt;a href="https://cosmin.us/vcf-automation-9-1-api-getting-started/"&gt;VCF Automation 9.1 API getting started&lt;/a&gt; post, I spent some time in the in-product API Help Center — log into the VM Apps tenant, click your username, &lt;strong&gt;API Help Center &amp;gt; Explore Automation APIs&lt;/strong&gt;. The Swagger pages render fine, but the moment you try an actual call, it fails. The &lt;strong&gt;Servers&lt;/strong&gt; box for the VCF Automation (Blueprint) API shows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://localhost:8600
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Swagger UI builds Try It Out requests from &lt;code&gt;servers[0].url&lt;/code&gt;, so the browser dutifully sends every request to port 8600 on &lt;em&gt;your own workstation&lt;/em&gt;. Connection refused, every time. The documentation is correct; the advertised host is not.&lt;/p&gt;
&lt;p&gt;This post is about finding where that value actually comes from — which took three wrong turns through the appliance&amp;rsquo;s Kubernetes internals — and the override that finally fixed it without touching a container image. The short version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;localhost:8600&lt;/code&gt; value is &lt;strong&gt;not in any configuration on the appliance&lt;/strong&gt;. A grep across a full support-bundle cluster dump finds no ConfigMap, Helm value, gateway route, or forwarded-headers setting that produces it.&lt;/li&gt;
&lt;li&gt;The spec is a &lt;strong&gt;static JSON file generated by springdoc at build time&lt;/strong&gt; — the giveaway is its &lt;code&gt;&amp;quot;description&amp;quot;: &amp;quot;Generated server url&amp;quot;&lt;/code&gt; — and it ships &lt;strong&gt;as a classpath resource inside the Spring Boot application&lt;/strong&gt; in the &lt;code&gt;tango-blueprint-service-app&lt;/code&gt; pod. Whoever built it had the service listening on port 8600, and the URL froze there.&lt;/li&gt;
&lt;li&gt;The api-docs page itself is served by a &lt;em&gt;different&lt;/em&gt; pod than the spec, which is why the obvious nginx-level fixes go nowhere.&lt;/li&gt;
&lt;li&gt;The fix: mount a corrected copy of the spec from a ConfigMap and prepend &lt;code&gt;file:/override/&lt;/code&gt; to &lt;code&gt;spring.web.resources.static-locations&lt;/code&gt; via &lt;code&gt;JAVA_OPTS&lt;/code&gt;, so Spring serves the fixed file and never falls through to the copy shipped in the image. Survives pod restarts with nothing to reconcile it away; a product upgrade will wipe it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything below was done on a lab appliance running build &lt;code&gt;9.1.0.0100.25429499&lt;/code&gt;. This is an unsupported modification — more on that at the end.&lt;/p&gt;
&lt;h2 id="the-symptom"&gt;The Symptom&lt;/h2&gt;
&lt;p&gt;In the tenant API Help Center, open the VCF Automation API (the Blueprint one, version 2019-09-12). The spec renders, the paths are all correct, but the servers dropdown offers exactly one entry: &lt;code&gt;http://localhost:8600&lt;/code&gt;. There is no way to type a different URL — the dropdown only lists what the spec declares, and this spec declares one hardcoded server with no variables.&lt;/p&gt;
&lt;p&gt;Browser DevTools (Network tab) shows what the page actually loads. First an index of every API the Help Center knows about:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/tenant/&amp;lt;your-tenant&amp;gt;/api-docs/assets/content/soft/automation-apis.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That index maps each tile to a spec URL — about sixteen specs across a dozen backend services. The Blueprint entry points at:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/blueprint/api-docs/classic-apis/api-doc-public-classic-vra-2019-09-12.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that document contains the problem, verbatim:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;servers&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://localhost:8600&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Generated server url&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two details in that response are worth pausing on, because they shaped the whole investigation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;Generated server url&amp;quot;&lt;/code&gt; is the literal default description springdoc attaches when it auto-computes the server URL from the incoming request. So springdoc generated this — but if it were computed per request, it would show the appliance FQDN, not localhost.&lt;/li&gt;
&lt;li&gt;The response carries a &lt;code&gt;last-modified&lt;/code&gt; date from months before I ever opened the page, plus &lt;code&gt;accept-ranges: bytes&lt;/code&gt;. That is a static file being read off disk (or classpath), not a document rendered per request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Put together: someone ran the Blueprint service on a build machine, where it listened on &lt;code&gt;localhost:8600&lt;/code&gt;, exported the generated spec to a file, and shipped that file in the product. The URL froze at build time.&lt;/p&gt;
&lt;h2 id="ruling-out-configuration"&gt;Ruling Out Configuration&lt;/h2&gt;
&lt;p&gt;Before touching anything, I wanted to know whether some appliance setting produces or rewrites that value — because if it did, that would be the supported place to fix it.&lt;/p&gt;
&lt;p&gt;It does not. I had a full support bundle from this appliance handy (that is a story for another post), and the extracted cluster object dump is conclusive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The string &lt;code&gt;8600&lt;/code&gt; appears nowhere in the cluster as a port, URL, or config value. No Service exposes it, no container declares it, no ConfigMap mentions it. The only matches are UID substrings.&lt;/li&gt;
&lt;li&gt;There is no &lt;code&gt;springdoc&lt;/code&gt;, &lt;code&gt;server.forward-headers-strategy&lt;/code&gt;, or server-URL configuration in any ConfigMap in the &lt;code&gt;prelude&lt;/code&gt; or &lt;code&gt;vmsp-platform&lt;/code&gt; namespaces.&lt;/li&gt;
&lt;li&gt;The Envoy gateway routes (Gateway API &lt;code&gt;HTTPRoute&lt;/code&gt; objects) that publish the api-docs paths perform &lt;strong&gt;path rewrites only&lt;/strong&gt; — nothing rewrites hosts or response bodies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So there is no knob. The value lives in content, not configuration, and only Broadcom can fix it properly. Everything from here on is about making the appliance serve a corrected copy.&lt;/p&gt;
&lt;h2 id="wrong-turn-1-the-pod-that-serves-the-page-doesnt-serve-the-spec"&gt;Wrong Turn #1: The Pod That Serves the Page Doesn&amp;rsquo;t Serve the Spec&lt;/h2&gt;
&lt;p&gt;The api-docs page shell — the Angular app with the tiles — is served by the &lt;code&gt;cloud-automation-ui-app&lt;/code&gt; pod in the &lt;code&gt;prelude&lt;/code&gt; namespace. Its nginx config (ConfigMap &lt;code&gt;cloud-automation-ui&lt;/code&gt;) rewrites &lt;code&gt;/tenant/&amp;lt;org&amp;gt;/api-docs&lt;/code&gt; and &lt;code&gt;/provider/api-docs&lt;/code&gt; into static content under &lt;code&gt;/usr/share/nginx/html/dist&lt;/code&gt;. My first instinct was an nginx &lt;code&gt;sub_filter&lt;/code&gt; there, rewriting &lt;code&gt;localhost:8600&lt;/code&gt; in responses.&lt;/p&gt;
&lt;p&gt;Dead end, for a structural reason: the spec never passes through that pod. The gateway routes the two URLs to different backends entirely:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Path&lt;/th&gt;
					&lt;th&gt;HTTPRoute&lt;/th&gt;
					&lt;th&gt;Backend&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/tenant/&amp;lt;org&amp;gt;/api-docs/...&lt;/code&gt; (the page, the index JSON)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tenant-in-path-automation-ui&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cloud-automation-ui&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/blueprint/api-docs/...&lt;/code&gt; (the spec)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;blueprintapi-docs&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tango-blueprint:8080&lt;/code&gt;, prefix rewritten to &lt;code&gt;/api-docs/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A &lt;code&gt;sub_filter&lt;/code&gt; in the UI pod would rewrite a response it never sees. The actual owner of the spec is the Blueprint service itself: &lt;code&gt;Deployment/tango-blueprint-service-app&lt;/code&gt;, image &lt;code&gt;blueprint-webapp:9.1.0.0100.25429499&lt;/code&gt; — a Spring Boot app, which also explains the Spring Security headers on the spec response.&lt;/p&gt;
&lt;h2 id="wrong-turn-2-there-is-no-file-to-edit"&gt;Wrong Turn #2: There Is No File to Edit&lt;/h2&gt;
&lt;p&gt;Fine — exec into the right pod and &lt;code&gt;sed&lt;/code&gt; the file in place:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude &lt;span class="nb"&gt;exec&lt;/span&gt; deploy/tango-blueprint-service-app -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sh -c &lt;span class="s1"&gt;&amp;#39;find / -name &amp;#34;api-doc-public-classic-vra-*.json&amp;#34; -not -path &amp;#34;/proc/*&amp;#34; 2&amp;gt;/dev/null&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nothing. And looking for the application itself:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude &lt;span class="nb"&gt;exec&lt;/span&gt; deploy/tango-blueprint-service-app -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sh -c &lt;span class="s1"&gt;&amp;#39;find / -maxdepth 3 -name &amp;#34;*.jar&amp;#34; -not -path &amp;#34;/proc/*&amp;#34; 2&amp;gt;/dev/null | head&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/opt/bc-fips/bcutil-fips-2.1.5.jar
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/opt/bc-fips/bctls-fips-2.1.22.jar
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/opt/bc-fips/bcpkix-fips-2.1.10.jar
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/opt/bc-fips/bc-fips-2.1.2.jar
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/opt/bc-fips/bcmail-fips-2.1.6.jar
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/jdk/lib/jrt-fs.jar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Only the Bouncy Castle FIPS libraries and the JDK — the shallow search doesn&amp;rsquo;t even turn up the application archive itself. Wherever the application lives, the spec is packaged inside it as a classpath resource: &lt;code&gt;find&lt;/code&gt; sees nothing, and &lt;code&gt;sed&lt;/code&gt; has nothing to reach. Even if you dug out the archive, patched it, and re-packed it in the running container, the fix would be pointless: the JVM holds it open, so a restart is needed to pick up the change, and the restart is exactly what rebuilds the container filesystem from the pristine image. The fix would erase itself at the moment of application.&lt;/p&gt;
&lt;h2 id="wrong-turn-3-kubectl-edit-ate-my-java_opts"&gt;Wrong Turn #3: kubectl edit Ate My JAVA_OPTS&lt;/h2&gt;
&lt;p&gt;The mechanism that does work is Spring&amp;rsquo;s static resource resolution. Spring Boot serves static content from an ordered list of locations, and the first location that contains a matching file wins:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;spring.web.resources.static-locations =
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Setting that property &lt;em&gt;replaces&lt;/em&gt; the default list. So if I prepend a filesystem directory and re-append the defaults, a file I control shadows the identically-pathed resource on the application classpath — and everything else keeps being served from the classpath exactly as before.&lt;/p&gt;
&lt;p&gt;The path arithmetic works out neatly. After the gateway&amp;rsquo;s prefix rewrite, the backend sees the request as &lt;code&gt;/api-docs/classic-apis/api-doc-public-classic-vra-2019-09-12.json&lt;/code&gt;. With &lt;code&gt;file:/override/&lt;/code&gt; first in the list, Spring checks &lt;code&gt;/override/api-docs/classic-apis/api-doc-public-classic-vra-2019-09-12.json&lt;/code&gt; before falling through to the classpath. That is exactly where a ConfigMap mount can put a corrected copy.&lt;/p&gt;
&lt;p&gt;The wrong-turn part: my first attempt applied all of this with &lt;code&gt;kubectl edit deploy&lt;/code&gt;. The volume and volumeMount saved; the &lt;code&gt;JAVA_OPTS&lt;/code&gt; append &lt;strong&gt;silently did not&lt;/strong&gt;. That env value is one enormous line that already contains &lt;code&gt;$(SERVICE_ACCOUNT_SECRET)&lt;/code&gt; — literal Kubernetes variable-expansion syntax — and somewhere in the editor round-trip the appended text was lost; I never pinned down exactly where. (The same string is also a shell command-substitution landmine if you ever round-trip the value through a shell, which is why the fix below patches programmatically.) The pod rolled, the override directory appeared, and the spec still said &lt;code&gt;localhost:8600&lt;/code&gt; because the JVM never got the property. Verify the property reached the Deployment before concluding the approach failed.&lt;/p&gt;
&lt;h2 id="the-fix"&gt;The Fix&lt;/h2&gt;
&lt;p&gt;All commands run as root on the appliance, where &lt;code&gt;kubectl&lt;/code&gt; is already configured. Replace &lt;code&gt;vcfa.domain.com&lt;/code&gt; with your appliance FQDN.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Produce the corrected spec.&lt;/strong&gt; No spelunking inside the image needed — pull it through the gateway and fix the one string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk https://vcfa.domain.com/blueprint/api-docs/classic-apis/api-doc-public-classic-vra-2019-09-12.json &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;&amp;#39;s|http://localhost:8600|https://vcfa.domain.com|g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt; /root/api-doc-public-classic-vra-2019-09-12.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sanity-check it — zero remaining hits, and still valid JSON:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -c localhost:8600 /root/api-doc-public-classic-vra-2019-09-12.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m json.tool /root/api-doc-public-classic-vra-2019-09-12.json &amp;gt; /dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;valid json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. Create the ConfigMap and back up the Deployment:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude create configmap blueprint-apidocs-override &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --from-file&lt;span class="o"&gt;=&lt;/span&gt;/root/api-doc-public-classic-vra-2019-09-12.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude get deploy tango-blueprint-service-app -o yaml &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt; /root/tango-blueprint-deploy.backup.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3. Add the volume and mount.&lt;/strong&gt; A strategic merge patch merges named list entries, so this adds without disturbing the existing volumes and mounts:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude patch deploy tango-blueprint-service-app --type&lt;span class="o"&gt;=&lt;/span&gt;strategic -p &lt;span class="s1"&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; template:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; volumes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; - name: apidocs-override
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; configMap:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; name: blueprint-apidocs-override
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; containers:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; - name: tango-blueprint-service-app
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; volumeMounts:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; - name: apidocs-override
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; mountPath: /override/api-docs/classic-apis
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;4. Append the properties to JAVA_OPTS — programmatically.&lt;/strong&gt; This reads the current value, appends both the current (&lt;code&gt;spring.web.resources.static-locations&lt;/code&gt;) and the pre-2.4 (&lt;code&gt;spring.resources.static-locations&lt;/code&gt;) property names — whichever one this Spring Boot generation ignores does no harm — and writes a JSON patch. It refuses to double-apply:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude get deploy tango-blueprint-service-app -o json &amp;gt; /root/d.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 - &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;import json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;d = json.load(open(&amp;#34;/root/d.json&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;env = d[&amp;#34;spec&amp;#34;][&amp;#34;template&amp;#34;][&amp;#34;spec&amp;#34;][&amp;#34;containers&amp;#34;][0][&amp;#34;env&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;i = [n for n, e in enumerate(env) if e[&amp;#34;name&amp;#34;] == &amp;#34;JAVA_OPTS&amp;#34;][0]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;loc = &amp;#34;file:/override/,classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;add = &amp;#34; -Dspring.web.resources.static-locations=&amp;#34; + loc + &amp;#34; -Dspring.resources.static-locations=&amp;#34; + loc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;if &amp;#34;static-locations&amp;#34; in env[i][&amp;#34;value&amp;#34;]:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; json.dump([], open(&amp;#34;/root/patch.json&amp;#34;, &amp;#34;w&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; print(&amp;#34;already present - nothing to do&amp;#34;); raise SystemExit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;json.dump([{&amp;#34;op&amp;#34;: &amp;#34;replace&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;#34;path&amp;#34;: &amp;#34;/spec/template/spec/containers/0/env/%d/value&amp;#34; % i,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;#34;value&amp;#34;: env[i][&amp;#34;value&amp;#34;] + add}],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; open(&amp;#34;/root/patch.json&amp;#34;, &amp;#34;w&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;print(&amp;#34;patch written for env index&amp;#34;, i)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude patch deploy tango-blueprint-service-app --type&lt;span class="o"&gt;=&lt;/span&gt;json --patch-file&lt;span class="o"&gt;=&lt;/span&gt;/root/patch.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;5. Wait for the rollout.&lt;/strong&gt; The Deployment uses the &lt;code&gt;Recreate&lt;/code&gt; strategy with a single replica, so the Blueprint service is briefly down while the pod is replaced:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude rollout status deploy/tango-blueprint-service-app --timeout&lt;span class="o"&gt;=&lt;/span&gt;180s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="verification"&gt;Verification&lt;/h2&gt;
&lt;p&gt;Learned from wrong turn #3: verify each layer, not just the end result.&lt;/p&gt;
&lt;p&gt;The property is on the Deployment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude get deploy tango-blueprint-service-app &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{.spec.template.spec.containers[0].env[?(@.name==&amp;#34;JAVA_OPTS&amp;#34;)].value}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; tr &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -i static
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-Dspring.web.resources.static-locations=file:/override/,classpath:/META-INF/resources/,...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-Dspring.resources.static-locations=file:/override/,classpath:/META-INF/resources/,...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The override file is mounted in the new pod:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude &lt;span class="nb"&gt;exec&lt;/span&gt; deploy/tango-blueprint-service-app -c tango-blueprint-service-app -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ls -la /override/api-docs/classic-apis/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the actual test — the spec as served through the gateway:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk https://vcfa.domain.com/blueprint/api-docs/classic-apis/api-doc-public-classic-vra-2019-09-12.json &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -A2 &lt;span class="s1"&gt;&amp;#39;&amp;#34;servers&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;servers&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://vcfa.domain.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hard-reload the API Help Center page (the SPA caches aggressively) and the Servers box shows the appliance FQDN. Try It Out now sends requests where they belong.&lt;/p&gt;
&lt;h2 id="what-this-does-and-does-not-fix"&gt;What This Does and Does Not Fix&lt;/h2&gt;
&lt;p&gt;Worth being precise here, because the Help Center is bigger than one spec:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fixed:&lt;/strong&gt; the 2019-09-12 Blueprint spec shown above. Its 2019-01-15 sibling from the same &lt;code&gt;/blueprint/api-docs/classic-apis/&lt;/code&gt; directory — if it shows the same URL — is the same fix: repeat step 1 for it, then rebuild the ConfigMap with both files: &lt;code&gt;kubectl -n prelude create configmap blueprint-apidocs-override --from-file=/root/api-doc-public-classic-vra-2019-09-12.json --from-file=/root/api-doc-public-classic-vra-2019-01-15.json --dry-run=client -o yaml | kubectl -n prelude replace -f -&lt;/code&gt;. The mounted volume picks up the change on its own within a minute or so; no restart needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Same pattern, different pod:&lt;/strong&gt; the catalog, deployment, and policies tiles point at &lt;code&gt;api-doc-public-classic-vra-*.yaml&lt;/code&gt; files under &lt;code&gt;/catalog/api-docs/classic-apis/&lt;/code&gt; — the same baked-file shape, owned by the catalog service. If those show the same URL, it is the same fix against that Deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Possibly not broken at all:&lt;/strong&gt; several tiles point at live springdoc endpoints (&lt;code&gt;/identity/doc/v3/api-docs&lt;/code&gt;, &lt;code&gt;/iaas-api/swagger/v3/api-docs/...&lt;/code&gt;, &lt;code&gt;/project-service/api/swagger/v3/api-docs/...&lt;/code&gt;). Those generate their spec per request, so check before assuming. A quick &lt;code&gt;curl ... | jq .servers&lt;/code&gt; against each URL from the index tells you which ones need attention.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="caveats"&gt;Caveats&lt;/h2&gt;
&lt;p&gt;This is an unsupported modification to a Broadcom appliance, and it behaves accordingly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It survives pod restarts, and nothing reconciles it away.&lt;/strong&gt; The fix lives in the Deployment spec, so every pod the Deployment creates from now on gets the mount and the property — the verified state above is itself a freshly recreated pod. The &lt;code&gt;tango-blueprint-service&lt;/code&gt; Helm release is deployed once at install/upgrade time with nothing reconciling it continuously, so it should come back intact after an appliance reboot as well; re-check the spec once after your next reboot to confirm. (Do not assume this generalizes: several other components in the same namespace &lt;em&gt;are&lt;/em&gt; Flux-managed and would revert an edit within minutes.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A product upgrade will wipe it.&lt;/strong&gt; The LCM re-renders the Deployment from the new chart. Expect to re-apply, and re-check after any lifecycle operation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Roll back at any time&lt;/strong&gt; with the backup from step 2. Not with &lt;code&gt;kubectl apply&lt;/code&gt;, though — the backup carries the pre-patch &lt;code&gt;resourceVersion&lt;/code&gt; (so the apply is rejected as a conflict), and client-side apply against a Helm-created object has no &lt;code&gt;last-applied-configuration&lt;/code&gt; to prune from, so the added volume would survive the merge and the next pod would wedge on the then-deleted ConfigMap. &lt;code&gt;kubectl replace&lt;/code&gt; does a full PUT and removes the volume, mount, and &lt;code&gt;JAVA_OPTS&lt;/code&gt; additions in one shot; strip the &lt;code&gt;resourceVersion&lt;/code&gt; so the PUT is unconditional, and delete the ConfigMap only after the Deployment no longer references it:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sed &lt;span class="s1"&gt;&amp;#39;/^ resourceVersion:/d&amp;#39;&lt;/span&gt; /root/tango-blueprint-deploy.backup.yaml &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; kubectl -n prelude replace -f -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude rollout status deploy/tango-blueprint-service-app --timeout&lt;span class="o"&gt;=&lt;/span&gt;180s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n prelude delete configmap blueprint-apidocs-override
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;File an SR anyway.&lt;/strong&gt; As of this writing there is no KB and nothing in the 9.1 release notes for this — the proper fix has to ship in the product, and that only happens if it gets reported. Reference the build number and the &lt;code&gt;&amp;quot;Generated server url&amp;quot;&lt;/code&gt; value in the spec.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you only need a working Try It Out for yourself and would rather not touch the appliance at all, there is a zero-footprint alternative: download the spec, fix &lt;code&gt;servers[0].url&lt;/code&gt; in the copy, and import it into Postman — it seeds the collection&amp;rsquo;s &lt;code&gt;baseUrl&lt;/code&gt; from the spec, and auth works as described in the &lt;a href="https://cosmin.us/vcf-automation-9-1-api-getting-started/"&gt;getting started post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="final-notes"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;The frustrating part of this bug is how little is actually wrong: every path in the spec is correct, authentication is correct, the API itself works. One string, frozen at build time on some build machine where the Blueprint service happened to listen on port 8600, breaks the one feature the page exists to provide.&lt;/p&gt;
&lt;p&gt;The investigation pattern is the reusable part. When a value appears in a rendered page and nothing in the configuration produces it, stop grepping ConfigMaps and follow the bytes instead: DevTools tells you the exact URL, the response headers tell you whether it is static or generated, and the gateway routes tell you which pod actually owns it. In this case each of those three steps invalidated an otherwise-reasonable fix — the nginx rewrite targeted the wrong pod, the in-pod &lt;code&gt;sed&lt;/code&gt; targeted a file that does not exist, and the editor-based patch failed silently — before the resource-shadowing approach landed on the one layer where the platform genuinely offers an override.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cosmin.us/vcf-automation-9-1-api-getting-started/"&gt;VCF Automation 9.1 API Getting Started&lt;/a&gt; — authentication against the same appliance, and where the API docs moved in 9.1&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/administration-sdks-cli-and-tools/what-are-the-automation-apis-and-how-do-i-use-them_1.html"&gt;Broadcom TechDocs — What Are the Automation APIs and How Do I Use Them (VCF 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.broadcom.com/xapis/org-management-vm-apps-org/latest/"&gt;VM Apps Org APIs on the Broadcom Developer Portal&lt;/a&gt; — the hosted alternative to the in-product Help Center&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-boot/reference/web/servlet.html#web.servlet.spring-mvc.static-content"&gt;Spring Boot Reference — Serving Static Content&lt;/a&gt; — the &lt;code&gt;static-locations&lt;/code&gt; resolution order this fix relies on&lt;/li&gt;
&lt;li&gt;&lt;a href="https://springdoc.org/faq.html"&gt;springdoc-openapi FAQ&lt;/a&gt; — how the &amp;ldquo;Generated server url&amp;rdquo; value is computed, and why it goes stale behind a proxy&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>VCF Automation 9.1 API Getting Started</title><link>https://cosmin.us/vcf-automation-9-1-api-getting-started/</link><pubDate>Thu, 30 Jul 2026 07:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/vcf-automation-9-1-api-getting-started/</guid><description>&lt;p&gt;Back in 2022 I wrote &lt;a href="https://cosmin.us/vra-8-api-getting-started/"&gt;vRA 8 API getting started&lt;/a&gt; — two curl calls and you had a bearer token. Since then the product has been renamed twice (vRealize Automation → VMware Aria Automation → VCF Automation) and, more importantly for anyone with scripts in production, the authentication flow changed in VCF 9.x. Neither of the two calls from that post works against a 9.1 appliance.&lt;/p&gt;
&lt;p&gt;This is the 9.1 version of the same post: the minimum you need to do before you can actually query the API, with curl.&lt;/p&gt;</description><content:encoded>&lt;p&gt;Back in 2022 I wrote &lt;a href="https://cosmin.us/vra-8-api-getting-started/"&gt;vRA 8 API getting started&lt;/a&gt; — two curl calls and you had a bearer token. Since then the product has been renamed twice (vRealize Automation → VMware Aria Automation → VCF Automation) and, more importantly for anyone with scripts in production, the authentication flow changed in VCF 9.x. Neither of the two calls from that post works against a 9.1 appliance.&lt;/p&gt;
&lt;p&gt;This is the 9.1 version of the same post: the minimum you need to do before you can actually query the API, with curl.&lt;/p&gt;
&lt;h2 id="what-changed-since-vra-8"&gt;What Changed Since vRA 8&lt;/h2&gt;
&lt;p&gt;The short version, before the steps:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;vRA 8.x&lt;/th&gt;
					&lt;th&gt;VCF Automation 9.1 (VM Apps org)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Refresh token&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;POST /csp/gateway/am/api/login?access_token&lt;/code&gt; with username/password&lt;/td&gt;
					&lt;td&gt;Generated in the UI (&lt;strong&gt;My Account &amp;gt; API Tokens&lt;/strong&gt;) — no programmatic call&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Bearer token&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;POST /iaas/api/login&lt;/code&gt; with the refresh token&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;POST /tm/oauth/tenant/{tenant}/token&lt;/code&gt;, standard OAuth &lt;code&gt;grant_type=refresh_token&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Bearer lifetime&lt;/td&gt;
					&lt;td&gt;~8 hours&lt;/td&gt;
					&lt;td&gt;1 hour&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Refresh token lifetime&lt;/td&gt;
					&lt;td&gt;90 days&lt;/td&gt;
					&lt;td&gt;90 days by default (configurable, at creation time only)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;API docs&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;https://vra_url/automation-ui/api-docs/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;In-product &lt;strong&gt;API Help Center&lt;/strong&gt;, plus developer.broadcom.com&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The two things that break existing scripts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;You cannot get a refresh token with username and password anymore.&lt;/strong&gt; The docs are explicit: &amp;ldquo;As of VCF 9.0, the Identity Service API functionality has changed and is no longer used to obtain a refresh token programmatically.&amp;rdquo; The refresh token is now an &lt;strong&gt;API token&lt;/strong&gt; you generate once in the UI and treat like a credential.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;There is a tenant in the URL now.&lt;/strong&gt; VCF Automation 9 has a provider/tenant model. Everything that used to be vRA lives inside a &lt;strong&gt;VM Apps organization&lt;/strong&gt;, and the token endpoint is scoped to that org name — so step zero is finding out what yours is called.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-0-find-your-vm-apps-tenant-name"&gt;Step 0: Find Your VM Apps Tenant Name&lt;/h2&gt;
&lt;p&gt;Log into the Provider Management Portal (&lt;code&gt;https://vcfa.domain.com&lt;/code&gt;, the &lt;code&gt;admin&lt;/code&gt; account) and go to &lt;strong&gt;Organizations&lt;/strong&gt;. Note two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The name of your VM Apps organization — if you upgraded from Aria Automation 8.x, this is the classic tenant that was created during migration (the docs use &lt;code&gt;vcf-e2e-vidm&lt;/code&gt; as an example).&lt;/li&gt;
&lt;li&gt;The FQDN of the appliance itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You need both for every call that follows.&lt;/p&gt;
&lt;h2 id="step-1-generate-an-api-token-the-new-refresh-token"&gt;Step 1: Generate an API Token (the New Refresh Token)&lt;/h2&gt;
&lt;p&gt;This is the part that moved from curl to the UI:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the VCF Automation UI at &lt;code&gt;https://vcfa.domain.com&lt;/code&gt; and switch the organization to your VM Apps tenant.&lt;/li&gt;
&lt;li&gt;Log in with a tenant account (it needs to be an org member with at least the &lt;code&gt;user&lt;/code&gt; service role — an &lt;code&gt;org_owner&lt;/code&gt; works fine).&lt;/li&gt;
&lt;li&gt;Click your name in the top-right corner and select &lt;strong&gt;My Account&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;API Tokens&lt;/strong&gt; tab, click &lt;strong&gt;New&lt;/strong&gt;, give the token a name, and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy the token — it is shown once.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By default the token lives for 129,600 minutes (90 days). If you want a different lifespan, set it &lt;strong&gt;before&lt;/strong&gt; creating the token under &lt;strong&gt;General Settings &amp;gt; Timeouts&lt;/strong&gt; in the Provider Management Portal (&lt;code&gt;0&lt;/code&gt; means unlimited) — the lifetime is baked in at creation and cannot be changed retroactively. To kill a token early, revoke it from the same UI or via &lt;code&gt;DELETE /cloudapi/1.0.0/tokens/urn:vcloud:token:&amp;lt;token_id&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="step-2-exchange-the-api-token-for-an-access-token"&gt;Step 2: Exchange the API Token for an Access Token&lt;/h2&gt;
&lt;p&gt;This replaces the old &lt;code&gt;POST /iaas/api/login&lt;/code&gt;. The endpoint is a standard OAuth token endpoint, scoped to your tenant:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k --location --request POST &lt;span class="s1"&gt;&amp;#39;https://vcfa.domain.com/tm/oauth/tenant/your-vm-apps-tenant/token&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --header &lt;span class="s1"&gt;&amp;#39;Content-Type: application/x-www-form-urlencoded&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --header &lt;span class="s1"&gt;&amp;#39;Accept: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --data-urlencode &lt;span class="s1"&gt;&amp;#39;grant_type=refresh_token&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --data-urlencode &lt;span class="s1"&gt;&amp;#39;refresh_token=YOUR_API_TOKEN&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The response contains the bearer token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;access_token&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eyJhbGciOi...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;token_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Bearer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;expires_in&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;refresh_token&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;YOUR_API_TOKEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The access token expires after one hour — noticeably shorter than the ~8 hours you got from &lt;code&gt;/iaas/api/login&lt;/code&gt; in 8.x, so long-running scripts should be prepared to re-run the exchange.&lt;/p&gt;
&lt;p&gt;For scripting, the whole thing collapses into one line with &lt;code&gt;jq&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -sk --request POST &lt;span class="s2"&gt;&amp;#34;https://&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_HOST&lt;/span&gt;&lt;span class="s2"&gt;/tm/oauth/tenant/&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_TENANT&lt;/span&gt;&lt;span class="s2"&gt;/token&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/x-www-form-urlencoded&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Accept: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --data-urlencode &lt;span class="s1"&gt;&amp;#39;grant_type=refresh_token&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --data-urlencode &lt;span class="s2"&gt;&amp;#34;refresh_token=&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_API_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq -r .access_token&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-3-use-it"&gt;Step 3: Use It&lt;/h2&gt;
&lt;p&gt;The good news: once you have the access token, the API surface you know from vRA 8 is still there. The 9.1 programming guide tutorials use the same service endpoints — &lt;code&gt;/iaas/api&lt;/code&gt;, &lt;code&gt;/catalog/api&lt;/code&gt;, &lt;code&gt;/deployment/api&lt;/code&gt;, &lt;code&gt;/blueprint/api&lt;/code&gt;, &lt;code&gt;/abx&lt;/code&gt;, the Orchestrator gateway — with a plain bearer header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="s2"&gt;&amp;#34;https://&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_HOST&lt;/span&gt;&lt;span class="s2"&gt;/iaas/api/projects&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If a call comes back &lt;code&gt;403&lt;/code&gt;, verify what the account actually is inside the org. The docs still expose the CSP gateway for this (note the &lt;code&gt;csp-auth-token&lt;/code&gt; header instead of &lt;code&gt;Authorization&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# your org id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="s2"&gt;&amp;#34;https://&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_HOST&lt;/span&gt;&lt;span class="s2"&gt;/csp/gateway/am/api/loggedin/user/orgs&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;csp-auth-token: &lt;/span&gt;&lt;span class="nv"&gt;$ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# org role: must be org_owner or org_member&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="s2"&gt;&amp;#34;https://&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_HOST&lt;/span&gt;&lt;span class="s2"&gt;/csp/gateway/am/api/loggedin/user/orgs/&lt;/span&gt;&lt;span class="nv"&gt;$ORG_ID&lt;/span&gt;&lt;span class="s2"&gt;/roles&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;csp-auth-token: &lt;/span&gt;&lt;span class="nv"&gt;$ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# service roles: must include at least &amp;#34;user&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="s2"&gt;&amp;#34;https://&lt;/span&gt;&lt;span class="nv"&gt;$VCFA_HOST&lt;/span&gt;&lt;span class="s2"&gt;/csp/gateway/am/api/loggedin/user/orgs/&lt;/span&gt;&lt;span class="nv"&gt;$ORG_ID&lt;/span&gt;&lt;span class="s2"&gt;/service-roles&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;csp-auth-token: &lt;/span&gt;&lt;span class="nv"&gt;$ACCESS_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One caveat from Broadcom: scripts that authenticate against &lt;strong&gt;new&lt;/strong&gt; VM Apps organizations (created in 9.x, not upgraded from 8.x) should use the VCF Automation Provider Management Access Control APIs rather than the CSP APIs — treat the CSP gateway as a compatibility surface, not a foundation for new tooling.&lt;/p&gt;
&lt;h2 id="where-the-api-docs-moved"&gt;Where the API Docs Moved&lt;/h2&gt;
&lt;p&gt;The old in-product Swagger at &lt;code&gt;https://vra_url/automation-ui/api-docs/&lt;/code&gt; and the developer.vmware.com portal are both gone. In 9.1:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;In-product:&lt;/strong&gt; log into the VM Apps tenant, click your username in the top-right corner, and open &lt;strong&gt;API Help Center &amp;gt; Explore Automation APIs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Online:&lt;/strong&gt; the &lt;a href="https://developer.broadcom.com/xapis/org-management-vm-apps-org/latest/"&gt;VM Apps Org APIs on the Broadcom Developer Portal&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also worth knowing before you port scripts from 8.x: the Pipelines/Code Stream APIs (&lt;code&gt;/codestream/api/*&lt;/code&gt;), the CMX Kubernetes APIs, and the Migration Assessment APIs are removed in 9.1.&lt;/p&gt;
&lt;h2 id="a-note-on-all-apps-organizations"&gt;A Note on All Apps Organizations&lt;/h2&gt;
&lt;p&gt;Everything above covers the VM Apps organization — the classic vRA-shaped experience. The new &lt;strong&gt;All Apps&lt;/strong&gt; side of VCF Automation 9 (the Cloud Director-heritage Tenant Manager) authenticates differently again: provider tokens come from &lt;code&gt;POST /oauth/provider/token&lt;/code&gt;, and there is a session endpoint at &lt;code&gt;/cloudapi/1.0.0/sessions/provider&lt;/code&gt; that does accept basic auth and returns the bearer in the &lt;code&gt;x-vmware-vcloud-access-token&lt;/code&gt; response header. That API family deserves its own post; Christian Ferber has a good writeup linked below in the meantime.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/administration-sdks-cli-and-tools/what-are-the-automation-apis-and-how-do-i-use-them_1.html"&gt;Broadcom TechDocs — What Are the Automation APIs and How Do I Use Them (VCF 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/administration-sdks-cli-and-tools/what-are-the-automation-apis-and-how-do-i-use-them_1/getting-your-authentication-token/getting-a-refresh-token-for-the-vm-apps-tenant.html"&gt;Broadcom TechDocs — Getting a Refresh Token for the VM Apps Tenant (VCF 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/administration-sdks-cli-and-tools/what-are-the-automation-apis-and-how-do-i-use-them_1/getting-your-authentication-token/get-your-access-token-for-vra-8-x.html"&gt;Broadcom TechDocs — Get Your Access Token for the VCF Automation VM Apps API (VCF 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/administration-sdks-cli-and-tools/what-are-the-automation-apis-and-how-do-i-use-them_1/getting-your-authentication-token/verify-user-roles.html"&gt;Broadcom TechDocs — Verify User Roles (VCF 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/administration-sdks-cli-and-tools/what-are-the-automation-apis-and-how-do-i-use-them_1/getting-your-authentication-token/changing-the-lifetime-setting-of-an-api-token.html"&gt;Broadcom TechDocs — Changing the Lifetime Setting of an API Token (VCF 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vrealize.it/2025/11/06/vcf-automation-9-api-access/"&gt;vrealize.it — VCF Automation 9 API Access (All Apps / provider-side authentication)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Fixing a VCF Operations 9.1 Express Patch Stuck at Installing PAK File: An Empty /etc/yum.repos.d and Four Layers of Upgrade State</title><link>https://cosmin.us/fixing-vcf-operations-9-1-express-patch-stuck-installing-pak-file/</link><pubDate>Wed, 29 Jul 2026 17:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/fixing-vcf-operations-9-1-express-patch-stuck-installing-pak-file/</guid><description>&lt;p&gt;The &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;9.1.0.0300 patch went through this lab without drama&lt;/a&gt; at the beginning of July. Three weeks later, the 9.1.0.0400 express patch for VCF Operations hit the same single-node appliance and stopped dead at &lt;strong&gt;Installing PAK file&lt;/strong&gt; — no progress, no error, and a reboot later, still nothing. What followed was a proper archaeology dig through every layer of state the upgrade machinery keeps, and almost every layer turned out to be holding a different version of the truth.&lt;/p&gt;</description><content:encoded>&lt;p&gt;The &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;9.1.0.0300 patch went through this lab without drama&lt;/a&gt; at the beginning of July. Three weeks later, the 9.1.0.0400 express patch for VCF Operations hit the same single-node appliance and stopped dead at &lt;strong&gt;Installing PAK file&lt;/strong&gt; — no progress, no error, and a reboot later, still nothing. What followed was a proper archaeology dig through every layer of state the upgrade machinery keeps, and almost every layer turned out to be holding a different version of the truth.&lt;/p&gt;
&lt;p&gt;The short version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The upgrade died &lt;strong&gt;two seconds&lt;/strong&gt; into the OS update phase because the glob over &lt;code&gt;/etc/yum.repos.d/*&lt;/code&gt; matched &lt;strong&gt;nothing&lt;/strong&gt; — the directory had been emptied (or removed; the error is identical either way) — and &lt;code&gt;sed&lt;/code&gt; exiting 2 on it is fatal to &lt;code&gt;updateCoordinator.py&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The failure was recorded in pakManager&amp;rsquo;s results file, and from then on a &lt;strong&gt;retry guard&lt;/strong&gt; rejected every new attempt in milliseconds — while CaSA kept retrying roughly &lt;strong&gt;every 11–12 seconds&lt;/strong&gt;, over 1,100 times in the retained logs alone.&lt;/li&gt;
&lt;li&gt;The UI said &lt;em&gt;Installing&lt;/em&gt; the whole time because CaSA&amp;rsquo;s own pak history had the &lt;code&gt;APPLY_OS_UPDATE&lt;/code&gt; operation with a &lt;strong&gt;null &lt;code&gt;completed_time&lt;/code&gt;&lt;/strong&gt; — stale state, not activity.&lt;/li&gt;
&lt;li&gt;Rebooting did not retry anything; it just rebuilt the same wedge, with the cluster still offline because &lt;em&gt;offline&lt;/em&gt; is persisted state.&lt;/li&gt;
&lt;li&gt;The recovery attempts then failed in two brand-new ways: a &lt;strong&gt;409 &lt;code&gt;pak_installation_active&lt;/code&gt;&lt;/strong&gt; raised when the precheck&amp;rsquo;s state check collided with the assessment install the precheck itself had just launched, and a &lt;strong&gt;NullPointerException&lt;/strong&gt; when LCM asked CaSA for an upgrade task that no longer existed anywhere.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Versions in play: VCF Operations &lt;strong&gt;9.1.0.0300.25531073&lt;/strong&gt; upgrading to &lt;strong&gt;9.1.0.0400.25541550&lt;/strong&gt;, on a single-node deployment, driven by the express patch workflow in fleet management. All timestamps below are UTC, straight from the logs.&lt;/p&gt;
&lt;h2 id="the-symptom-installing-pak-file-forever"&gt;The Symptom: Installing PAK File, Forever&lt;/h2&gt;
&lt;p&gt;The express patch staged cleanly and started applying, then sat at &lt;em&gt;Installing PAK file&lt;/em&gt; for hours. A reboot later it still showed the same thing. Time to stop watching the progress bar and go look at what the appliance was actually doing.&lt;/p&gt;
&lt;p&gt;The places that matter for any PAK-based upgrade on a VCF Operations node:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;What&lt;/th&gt;
					&lt;th&gt;Where&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;pakManager action logs&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/log/vcops/log/pakManager/vcopsPakManager.root.&amp;lt;action&amp;gt;.log&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Per-PAK script stdout/stderr&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/log/vcops/log/pakManager/&amp;lt;pak-id&amp;gt;/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;PAK working state + results&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/db/pakRepoLocal/&amp;lt;pak-id&amp;gt;/&amp;lt;pak-id&amp;gt;.results&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;pakManager action history&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/db/pakRepoLocal/vcopsPakManagerCommonHistory.json&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CaSA logs + per-PAK history&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/log/vcops/log/casa/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Staged PAK binaries&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/db/casa/pak/dist_pak_files/VA_LINUX/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The PAK for this patch is &lt;code&gt;vRealizeOperationsManagerEnterprise-910040025541550&lt;/code&gt; — the ID is the version string with the dots removed, &lt;code&gt;9.1.0.0400.25541550&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="finding-the-loop"&gt;Finding the Loop&lt;/h2&gt;
&lt;p&gt;The first thing that stood out was not an error message but a rotation pattern. &lt;code&gt;vcopsPakManager.root.apply_os_update.log&lt;/code&gt; keeps twenty rotated files of 1 MB each, and every one of them was less than four hours old — the log was burning a full rotation roughly every 11 minutes. Something was running &lt;code&gt;apply_os_update&lt;/code&gt; constantly.&lt;/p&gt;
&lt;p&gt;Each attempt ended the same way, in milliseconds (log prefixes trimmed for width here and throughout):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[VerifyActionNotRunningOrRun] - apply_os_update result: &amp;#34;Failed&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[VerifyActionNotRunningOrRun] - apply_os_update exit code: &amp;#34;1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[HandleError] - Exiting with exit code: 1, message: The action: apply_os_update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;on the pak file: &amp;#34;vRealizeOperationsManagerEnterprise-910040025541550&amp;#34; previously
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;failed, cleanup the system and remove_history before attempting again--exiting
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is pakManager&amp;rsquo;s retry guard: once an action has &lt;code&gt;Failed&lt;/code&gt; in the PAK&amp;rsquo;s &lt;code&gt;.results&lt;/code&gt; file, every subsequent invocation is rejected before any script runs. And CaSA — the admin/cluster service that orchestrates PAK operations — was invoking it again roughly every 11–12 seconds, each time via:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/bin/sudo -n /usr/lib/vmware-python-3/bin/python
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /usr/lib/vmware-vcopssuite/utilities/pakManager/bin/vcopsPakManager.py
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --action apply_os_update --pak vRealizeOperationsManagerEnterprise-910040025541550
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --json --force_content_update true --roles ADMIN,DATA,UI
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The retained logs held &lt;strong&gt;1,125&lt;/strong&gt; rejections spanning just under four hours — and CaSA&amp;rsquo;s logs show the loop reaching back to the previous evening. An install loop where every iteration is refused on arrival: the upgrade could not move forward and would never stop trying. So the interesting question became: what failed the &lt;em&gt;first&lt;/em&gt; time?&lt;/p&gt;
&lt;h2 id="the-root-cause-one-sed-against-an-empty-directory"&gt;The Root Cause: One sed Against an Empty Directory&lt;/h2&gt;
&lt;p&gt;CaSA&amp;rsquo;s operation history gives the setup, and the per-PAK stderr log — &lt;code&gt;/storage/log/vcops/log/pakManager/vRealizeOperationsManagerEnterprise-910040025541550/apply_os_update_stderr.log&lt;/code&gt; — gives the kill. The timeline on July 28:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;17:42:27 → 17:42:33&lt;/code&gt; — validation runs and passes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;17:42:45 → 17:44:29&lt;/code&gt; — &lt;code&gt;BRING_CLUSTER_OFFLINE&lt;/code&gt; completes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;17:44:37&lt;/code&gt; — &lt;code&gt;APPLY_OS_UPDATE&lt;/code&gt; starts&lt;/li&gt;
&lt;li&gt;&lt;code&gt;17:44:39&lt;/code&gt; — the very first command of the VA upgrade phase runs, and dies:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[b2b - updateCoordinator - os_upgrade] - Entering VA upgrade phase
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[b2b - updateCoordinator - os_upgrade] - Disable existing repositories
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[b2b - updateCoordinator - runScript] - Script command:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;/usr/bin/sed -i &amp;#34;s/enabled=1/enabled=0/g&amp;#34; /etc/yum.repos.d/*&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[b2b - updateCoordinator - runScript] - stderr:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;/usr/bin/sed: can&amp;#39;t read /etc/yum.repos.d/*: No such file or directory&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[b2b - updateCoordinator - runScript] - exit code: 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[b2b - updateCoordinator - programExit] - Unable to disable existing repositories for updates.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The glob over &lt;code&gt;/etc/yum.repos.d/*&lt;/code&gt; matched nothing — the shell passed the literal string through, &lt;code&gt;sed&lt;/code&gt; could not open it, and exited 2. That error is byte-identical whether the directory is empty or missing outright; by the time anyone could look, the files were back, so all the log proves is that there was nothing there to match. &lt;code&gt;updateCoordinator.py&lt;/code&gt; treats a non-zero exit here as fatal — reasonably, since it is about to drive &lt;code&gt;tdnf&lt;/code&gt; package operations and wants external repositories disabled first.&lt;/p&gt;
&lt;p&gt;Two details make this sting:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The upgrade does not need working external repos at all.&lt;/strong&gt; The very next steps in a healthy run copy the PAK&amp;rsquo;s own &lt;code&gt;local-pak-iso.repo&lt;/code&gt; into that directory and install everything from the ISO embedded in the PAK. The &lt;code&gt;sed&lt;/code&gt; only needs the glob to match &lt;em&gt;at least one file&lt;/em&gt;. A directory with nothing in it is the one state it cannot survive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This exact command succeeded three weeks earlier.&lt;/strong&gt; The 0300 patch on July 5 logged the identical &lt;code&gt;sed&lt;/code&gt; with an empty stderr and exit 0, then dropped &lt;code&gt;local-pak-iso.repo&lt;/code&gt; in place and carried on. So the directory was populated on July 5 and held nothing the glob could match by July 28 — something removed those files in between. More on the suspects at the end.&lt;/p&gt;
&lt;p&gt;One more log-reading trap: the failure &lt;em&gt;looks&lt;/em&gt; like it took ten minutes, because after the fatal error &lt;code&gt;updateCoordinator.py&lt;/code&gt; spends from &lt;code&gt;17:44:39&lt;/code&gt; to &lt;code&gt;17:54:47&lt;/code&gt; generating a support bundle before it exits. The &lt;code&gt;Failed&lt;/code&gt; result lands in the state files at &lt;code&gt;17:54:47&lt;/code&gt;, ten minutes after the two-second failure. If you correlate timestamps across logs, that gap is the tool being helpful, not the upgrade doing work.&lt;/p&gt;
&lt;h2 id="why-it-looked-stuck-instead-of-failed"&gt;Why It Looked Stuck Instead of Failed&lt;/h2&gt;
&lt;p&gt;Here is the part that turned a two-second failure into a day of confusion. The upgrade machinery keeps (at least) &lt;strong&gt;four separate stores of state&lt;/strong&gt;, and they disagreed:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Store&lt;/th&gt;
					&lt;th&gt;Path&lt;/th&gt;
					&lt;th&gt;Owner&lt;/th&gt;
					&lt;th&gt;What it said&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;PAK results file&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/db/pakRepoLocal/&amp;lt;pak-id&amp;gt;/&amp;lt;pak-id&amp;gt;.results&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;pakManager&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;apply_os_update_result: Failed&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Action history&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/db/pakRepoLocal/vcopsPakManagerCommonHistory.json&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;pakManager&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;apply_os_update: Failed&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Per-PAK operation history&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/storage/log/vcops/log/casa/casa_pak_history_&amp;lt;pak-id&amp;gt;.json&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;CaSA&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;APPLY_OS_UPDATE&lt;/code&gt; started, &lt;strong&gt;never completed&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Upgrade orchestrator documents&lt;/td&gt;
					&lt;td&gt;CaSA runtime&lt;/td&gt;
					&lt;td&gt;CaSA&lt;/td&gt;
					&lt;td&gt;operation active, keep retrying&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The UI renders from CaSA&amp;rsquo;s view. Its per-PAK history file ended like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;operation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;APPLY_OS_UPDATE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;start_time&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1785260677162&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;completed_time&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An operation with a start time and a null completion is, as far as the UI is concerned, &lt;em&gt;still installing&lt;/em&gt;. pakManager knew the upgrade had failed; CaSA never wrote a terminal result for the operation. Both were internally consistent. Neither was lying. They just were not the same state machine.&lt;/p&gt;
&lt;h2 id="why-rebooting-did-not-help"&gt;Why Rebooting Did Not Help&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;BRING_CLUSTER_OFFLINE&lt;/code&gt; had completed &lt;em&gt;before&lt;/em&gt; the failure, and offline is persisted admin state. After the reboot, the platform came up and refused to start its services on purpose — from the support bundle&amp;rsquo;s &lt;code&gt;sysenv/status.txt&lt;/code&gt;, captured while it was down:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vmware-vcops.service - LSB: vRealize Operations Platform
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Active: failed (Result: exit-code)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; vpostgres Cannot be started as the slice is offline
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; analytics Cannot be started as the slice is offline
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; collector Cannot be started as the slice is offline
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; api Cannot be started as the slice is offline
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the reboot traded a stuck upgrade on a running cluster for a stuck upgrade on a &lt;strong&gt;down&lt;/strong&gt; cluster. One genuinely useful side effect: the retry loop did not survive the restart. CaSA&amp;rsquo;s orchestrator documents came back empty — &lt;code&gt;current_pak_activity&lt;/code&gt; all nulls from the first post-reboot poll onward — so the hammering stopped; the last &lt;code&gt;apply_os_update&lt;/code&gt; attempt in the logs is from just before the reboot. But nothing resumed, either. The upgrade was now fully inert: no process running, no retries, UI still saying &lt;em&gt;Installing&lt;/em&gt;, and monitoring down with it.&lt;/p&gt;
&lt;p&gt;Bringing the cluster back online restored the data-collection side of the house — services active, slice &lt;code&gt;ONLINE&lt;/code&gt;, still on 9.1.0.0300 — while the upgrade state stayed exactly as wedged as before. Stale state does not age out. Waiting was never going to fix this.&lt;/p&gt;
&lt;h2 id="clearing-the-pak-state-cli-archaeology"&gt;Clearing the PAK State: CLI Archaeology&lt;/h2&gt;
&lt;p&gt;The guard message says what it wants: &lt;em&gt;&amp;ldquo;cleanup the system and remove_history before attempting again.&amp;rdquo;&lt;/em&gt; Turning that sentence into working commands took several tries, and the failed attempts are worth documenting because the CLI&amp;rsquo;s own error messages actively mislead.&lt;/p&gt;
&lt;p&gt;First lesson: run it with &lt;code&gt;python3&lt;/code&gt;. The script does not behave as a direct executable — invoked by path, the shell tries to interpret Python as shell (abridged, full path shortened):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vcopsPakManager.py: line 6: from: command not found
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vcopsPakManager.py: line 10: import: command not found
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vcopsPakManager.py: line 31: syntax error near unexpected token `(&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 /usr/lib/vmware-vcopssuite/utilities/pakManager/bin/vcopsPakManager.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run bare like that, it prints its full usage — which is where the trap sits. The guard message names &lt;code&gt;remove_history&lt;/code&gt; as if it were an action. It is not. It is a &lt;strong&gt;parameter of the &lt;code&gt;cleanup&lt;/code&gt; action&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cleanup - remove temp and working files
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; params:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --remove_history (ACTION|all) - remove the history for the action. The parameter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; is the action, or &amp;#34;all&amp;#34; to remove all history
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --remove_pak - removes the pak file and all history
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --skip_history_update - removes the pak without updating history
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The tour of error messages on the way there:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--action remove_history ... → Action: &amp;#34;remove_history&amp;#34; is not valid
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--action cleanup --remove_history --pak &amp;lt;pak&amp;gt; → Action: --pak is not supported for remove_history
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--action cleanup --remove_history → option --remove_history requires argument
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--action cleanup --remove_pak &amp;lt;pak&amp;gt; → No PAK file specified
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second one is the nasty one: &lt;code&gt;--remove_history&lt;/code&gt; requires an argument, so it silently consumed &lt;code&gt;--pak&lt;/code&gt; as its value and then complained that &lt;code&gt;--pak&lt;/code&gt; is not a valid action name. And the fourth shows &lt;code&gt;--remove_pak&lt;/code&gt; is a bare flag, not an option that takes the PAK name — the PAK always goes in &lt;code&gt;--pak&lt;/code&gt;. The forms that worked:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 /usr/lib/vmware-vcopssuite/utilities/pakManager/bin/vcopsPakManager.py &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --action cleanup --pak vRealizeOperationsManagerEnterprise-910040025541550
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 /usr/lib/vmware-vcopssuite/utilities/pakManager/bin/vcopsPakManager.py &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --action cleanup --remove_history all --pak vRealizeOperationsManagerEnterprise-910040025541550
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And one final surprise from the results file afterwards: &lt;code&gt;--remove_history all&lt;/code&gt; flipped a dozen keys to &lt;code&gt;&amp;quot;history removed&amp;quot;&lt;/code&gt; — but not the ones that mattered:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apply_adapter_result: history removed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apply_system_update_result: history removed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;new_validate_result: history removed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;validate_result: Failed ← survived
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apply_os_update_result: Failed ← survived
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apply_os_update_exit_code: 1 ← survived
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Several statuses outlived the removal — including, of all things, the &lt;code&gt;apply_os_update&lt;/code&gt; Failed/exit-1 pair that is exactly what the retry guard checks. Keep that in mind for the endgame below.&lt;/p&gt;
&lt;p&gt;While in there, this is also the moment to fix the actual root cause — put repo definitions back in &lt;code&gt;/etc/yum.repos.d/&lt;/code&gt;. A snapshot, a sibling appliance, or the Photon base image all work as a source; strictly speaking the upgrade only needs the glob to match one file, but restoring the stock set is the right fix. Healthy looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;local-pak-iso.repo photon-extras.repo photon-release.repo photon-srpms.repo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;photon-debuginfo.repo photon-iso.repo photon.repo photon-updates.repo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-precheck-409-colliding-with-your-own-assessment"&gt;The Precheck 409: Colliding With Your Own Assessment&lt;/h2&gt;
&lt;p&gt;With pakManager&amp;rsquo;s state cleared and the repo directory restored, the next upgrade precheck failed after about a minute with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;An unexpected error occurred in step ops_precheck. Reference Code: EEF54002.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;409: upgrade.verification.pak_installation_active
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PreConditionFailedCasaException: key=upgrade.verification.pak_installation_active
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;My first theory was the obvious one — that null-&lt;code&gt;completed_time&lt;/code&gt; entry still sitting in CaSA&amp;rsquo;s history. The CaSA log killed that theory and replaced it with something stranger. The precheck works by staging and installing an assessment PAK (&lt;code&gt;APUAT-for-814x&lt;/code&gt;) on the appliance, and the sequence around the failure reads:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;19:03:20 verifyConsistentState PAK_INSTALL → Cluster is in consistent state
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;19:03:21 APUAT-for-814x staged; validation, then apply_system_update begins
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;19:04:01 OrchestratorStatusDocument: current_pak_activity =
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {&amp;#34;pak_id&amp;#34;:&amp;#34;APUAT-for-814x-910025541563&amp;#34;,&amp;#34;operation_name&amp;#34;:&amp;#34;APPLY_SYSTEM_UPDATE&amp;#34;,&amp;#34;step&amp;#34;:12,...}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;19:04:01 verifyConsistentState PAK_INSTALL — again
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;19:04:02 Cluster state check failed: PreConditionFailedCasaException:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; key=upgrade.verification.pak_installation_active
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The same consistency check passed at &lt;code&gt;19:03:20&lt;/code&gt; with no PAK active, and failed 41 seconds later because a PAK installation &lt;em&gt;was&lt;/em&gt; genuinely active — &lt;strong&gt;the assessment install the precheck itself had just launched&lt;/strong&gt;. The stale enterprise-PAK entry was nowhere in it; CaSA was telling the truth about a collision between two halves of the same workflow. Meanwhile the assessment PAK, indifferent to the LCM step failing above it, ran to a clean finish in under twelve minutes — one more case of the layers not sharing a reality.&lt;/p&gt;
&lt;p&gt;Two useful tools on the CaSA side regardless. Its deployment state is queryable directly — and note it reported &lt;code&gt;&amp;quot;DONE&amp;quot;&lt;/code&gt; &lt;em&gt;while the 409 was still reproducible&lt;/em&gt;, so treat it as one more opinion, not an oracle:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin https://localhost/casa/deployment/cluster/installationstate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the non-destructive reset is restarting the service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl restart vmware-casa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I restarted CaSA and re-ran the precheck; this run went clean end to end — the assessment PAK staged, validated, installed, and cleaned up in thirteen minutes, and the step reported success. Whether the restart mattered or the retry simply avoided the earlier collision, I cannot prove from the logs — what the evidence does show is that the null-completion history entry was still in place, untouched, during the passing run. It was never the thing the 409 keyed on.&lt;/p&gt;
&lt;h2 id="the-nullpointerexception-asking-an-empty-orchestrator-for-a-task"&gt;The NullPointerException: Asking an Empty Orchestrator for a Task&lt;/h2&gt;
&lt;p&gt;Precheck green, one more attempt at the actual upgrade — and a new reference code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;An unexpected error occurred in step ops_apply_upgrade. Reference Code: 90AD4093.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Detail: Cannot invoke &amp;#34;com.broadcom.vcf.lcm.sddc.upgrade.sdk.client.appliance.model.Task.getId()&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;because &amp;#34;task&amp;#34; is null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An NPE in the LCM client is a product bug in the sense that it hides the real condition — but the underlying state is easy to read. CaSA&amp;rsquo;s orchestrator, logged on every status poll, was completely empty:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UpdateOrchestratorDocument={&amp;#34;casa_webapp_pak_id&amp;#34;:null,&amp;#34;casa_webapp_force_content_update&amp;#34;:false,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;casa_pak_status&amp;#34;:{}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OrchestratorStatusDocument: {&amp;#34;current_pak_activity&amp;#34;:{&amp;#34;pak_id&amp;#34;:null,&amp;#34;operation_name&amp;#34;:null,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;step&amp;#34;:0,&amp;#34;total_steps&amp;#34;:0,...},...,&amp;#34;queued_pak_ids&amp;#34;:[],&amp;#34;is_upgrade_orchestrator_active&amp;#34;:false,...}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the PAK&amp;rsquo;s working directory was down to two files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/storage/db/pakRepoLocal/vRealizeOperationsManagerEnterprise-910040025541550/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── manifest.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── vRealizeOperationsManagerEnterprise-910040025541550.results ← still says Failed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two separate subtractions added up to this. The reboot had emptied CaSA&amp;rsquo;s runtime documents and queue — that happened back at 12:43, and it is what stopped the retry loop. The &lt;code&gt;cleanup&lt;/code&gt; action had then removed the extracted PAK content from &lt;code&gt;pakRepoLocal&lt;/code&gt; — which is its job. Between the two, nothing on the appliance still described an in-flight upgrade, so when LCM told CaSA to proceed with the apply step, there was no registered PAK, no queued operation, and nothing to build a task from. CaSA returned no task; the LCM client dereferenced it; NPE. The pendulum had swung all the way from &lt;em&gt;state says failed, refuse to run&lt;/em&gt; to &lt;em&gt;no state at all, nothing to run&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The original binary survives, though — cleanup did not touch the staged PAK repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;locate .pak &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="m"&gt;0400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/storage/db/casa/pak/dist_pak_files/VA_LINUX/vRealizeOperationsManagerEnterprise-910040025541550.pak
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;plus the &lt;span class="m"&gt;0400&lt;/span&gt; license-server and cloud-proxy PAKs, trimmed&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="the-clean-path-out"&gt;The Clean Path Out&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ What follows is the planned remediation from this point, not a procedure I have verified end to end — which is exactly why step 1 exists. It is a lab; on a production deployment, this is the moment to take the reference codes (&lt;code&gt;EEF54002&lt;/code&gt;, &lt;code&gt;90AD4093&lt;/code&gt;) and the support bundle to an SR instead of editing state by hand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At this point the environment is: cluster online and collecting, on 9.1.0.0300; repo directory restored; precheck passing; PAK binary staged; and two pieces of stale state left — the &lt;code&gt;Failed&lt;/code&gt; survivor keys in the results file and the null-completion entry in CaSA&amp;rsquo;s per-PAK history. The way out is to stop half-clearing and reset the PAK completely, then let the workflow rebuild everything from the binary:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Snapshot the appliance.&lt;/strong&gt; The cluster is healthy right now; protect that before another attempt.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Remove the PAK and all of its history.&lt;/strong&gt; Per the usage text, &lt;code&gt;--remove_pak&lt;/code&gt; &amp;ldquo;removes the pak file and all history&amp;rdquo; — note this is the one cleanup variant I have not exercised, and given that &lt;code&gt;--remove_history all&lt;/code&gt; left the &lt;code&gt;Failed&lt;/code&gt; keys standing, do not take the usage text on faith:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 /usr/lib/vmware-vcopssuite/utilities/pakManager/bin/vcopsPakManager.py &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --action cleanup --remove_pak --pak vRealizeOperationsManagerEnterprise-910040025541550
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Afterwards, verify the &lt;code&gt;pakRepoLocal&lt;/code&gt; directory (with its &lt;code&gt;.results&lt;/code&gt; file) is actually gone, and that the staged binary in &lt;code&gt;/storage/db/casa/pak/dist_pak_files/VA_LINUX/&lt;/code&gt; is still there — if &lt;code&gt;--remove_pak&lt;/code&gt; reaches into the staged repository too, the workflow will need to re-download the PAK, which is inconvenient rather than fatal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clear CaSA&amp;rsquo;s per-PAK history&lt;/strong&gt; so the UI stops rendering the ghost &lt;em&gt;Installing&lt;/em&gt; operation and nothing can ever mistake it for an active install again — with the service stopped, move the stale file aside:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl stop vmware-casa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mv /storage/log/vcops/log/casa/casa_pak_history_vRealizeOperationsManagerEnterprise-910040025541550.json /root/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl start vmware-casa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Re-run the express patch from the beginning&lt;/strong&gt;, not as a resume. With the staged &lt;code&gt;.pak&lt;/code&gt; in place there is nothing to re-download; the workflow re-stages, re-validates, and — with &lt;code&gt;/etc/yum.repos.d/&lt;/code&gt; populated — should get past the two-second mark it never survived before. Expect the cluster to go offline again for the apply; that is the designed behavior (&lt;code&gt;bring_cluster_offline: true&lt;/code&gt; in the manifest), so schedule it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="verification"&gt;Verification&lt;/h2&gt;
&lt;p&gt;What success looks like once the re-run goes through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The appliance reports &lt;strong&gt;9.1.0.0400.25541550&lt;/strong&gt; and the express patch workflow completes without a reference code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curl -k -u admin https://localhost/casa/deployment/cluster/installationstate&lt;/code&gt; returns &lt;code&gt;&amp;quot;DONE&amp;quot;&lt;/code&gt; — with no failed step behind it this time.&lt;/li&gt;
&lt;li&gt;The new &lt;code&gt;casa_pak_history_vRealizeOperationsManagerEnterprise-910040025541550.json&lt;/code&gt; shows every operation, &lt;code&gt;APPLY_OS_UPDATE&lt;/code&gt; included, with a &lt;strong&gt;non-null&lt;/strong&gt; &lt;code&gt;completed_time&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The cluster slice is &lt;code&gt;ONLINE&lt;/code&gt; and collecting, and no &lt;code&gt;apply_os_update&lt;/code&gt; invocations appear in the pakManager logs after completion — the loop stays dead.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/yum.repos.d/&lt;/code&gt; is still populated. If it is empty again, find the culprit before the next patch does this all over.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-actually-emptied-etcyumreposd"&gt;What Actually Emptied /etc/yum.repos.d?&lt;/h2&gt;
&lt;p&gt;The unresolved thread. The directory was demonstrably populated on July 5 (the 0300 patch&amp;rsquo;s &lt;code&gt;sed&lt;/code&gt; succeeded and copied &lt;code&gt;local-pak-iso.repo&lt;/code&gt; into it) and demonstrably held nothing the glob could match on July 28. Neither observed upgrade run removed those files, and the PAK cleanup actions do not reach into &lt;code&gt;/etc&lt;/code&gt;. Something else did, in the three-week window between patches.&lt;/p&gt;
&lt;p&gt;The usual suspects are STIG/hardening remediation, a compliance scan &amp;ldquo;fixing&amp;rdquo; findings, or an overzealous cleanup script — and the hardening theory is less hypothetical than it sounds: the appliance ships a STIG-hardening Ansible role that the upgrade itself runs during &lt;code&gt;apply_system_update&lt;/code&gt;, and its task list includes a literal &lt;strong&gt;&amp;ldquo;Find yum repos&amp;rdquo;&lt;/strong&gt; step. A framework that polices exactly this directory demonstrably lives on the box; the July runs used it non-destructively, but whatever emptied the directory operated on the same territory. I have not caught the culprit in the act, so the case stays open.&lt;/p&gt;
&lt;p&gt;If that something is scheduled, it will do it again, and the next express patch will die the same two-second death. Before every PAK-based upgrade on a VCF Operations appliance, it now costs me one command to check:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls /etc/yum.repos.d/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Empty directory, no upgrade. It is that direct.&lt;/p&gt;
&lt;h2 id="takeaways"&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A stuck progress bar is a claim, not a fact.&lt;/strong&gt; The UI renders CaSA&amp;rsquo;s operation history; a null &lt;code&gt;completed_time&lt;/code&gt; displays as &lt;em&gt;Installing&lt;/em&gt; forever, even when the failure happened in two seconds and nothing has run since. The pakManager logs are where the truth lives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Know the four state stores&lt;/strong&gt; — per-PAK results file, &lt;code&gt;vcopsPakManagerCommonHistory.json&lt;/code&gt;, CaSA&amp;rsquo;s &lt;code&gt;casa_pak_history_&amp;lt;pak&amp;gt;.json&lt;/code&gt;, and CaSA&amp;rsquo;s runtime orchestrator documents. Every confusing symptom in this saga was two of them disagreeing — including the 409, where the precheck disagreed with the assessment install it had launched itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The retry guard message is honest but the CLI is not literal about it&lt;/strong&gt; — &lt;code&gt;remove_history&lt;/code&gt; is a parameter of &lt;code&gt;--action cleanup&lt;/code&gt;, not an action, and a missing argument makes it eat the next flag with a misleading error.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify what state-clearing commands actually cleared.&lt;/strong&gt; &lt;code&gt;--remove_history all&lt;/code&gt; reported nothing wrong while leaving the &lt;code&gt;apply_os_update: Failed&lt;/code&gt; keys — the exact ones the guard checks — untouched. After any cleanup, read the &lt;code&gt;.results&lt;/code&gt; file; do not assume.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cleanup&lt;/code&gt; without the root cause fixed is a trap.&lt;/strong&gt; The guard fires &lt;em&gt;before&lt;/em&gt; the script runs; clear the state while &lt;code&gt;/etc/yum.repos.d/&lt;/code&gt; is still empty and the next attempt fails in two seconds and re-arms it — from an offline cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reboots do not clear persisted upgrade state.&lt;/strong&gt; Offline stays offline, &lt;code&gt;Failed&lt;/code&gt; stays &lt;code&gt;Failed&lt;/code&gt;, and the null completion stays null. What a reboot &lt;em&gt;did&lt;/em&gt; do here is wipe CaSA&amp;rsquo;s runtime orchestrator — which killed the retry loop, made the wedge quieter and easier to misread as idle, and set up the eventual task-is-null NPE.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fatal-error support bundles skew timelines.&lt;/strong&gt; Nearly all of the ten minutes this &amp;ldquo;upgrade&amp;rdquo; ran were &lt;code&gt;generateSupportBundle.py&lt;/code&gt; executing after the fatal error. Read the stderr log inward from the first ERROR, not the timestamps outward.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check &lt;code&gt;/etc/yum.repos.d/&lt;/code&gt; before any PAK upgrade&lt;/strong&gt;, and find out what is emptying it — whatever removed those files will break the next patch identically if it runs again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="final-notes"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Every stage of this one was the same lesson wearing different clothes: the upgrade machinery is several loosely-coupled state machines, and each error message was one machine reporting its own local truth. pakManager said &lt;em&gt;previously failed&lt;/em&gt;; CaSA said &lt;em&gt;still installing&lt;/em&gt;; the precheck said &lt;em&gt;installation active&lt;/em&gt; — correctly, about the assessment install it had just started itself; LCM said &lt;em&gt;task is null&lt;/em&gt;. Every statement was locally accurate, none of them described the system, and the actual root cause — a directory that a three-week-old patch run proves used to be populated — never appeared in any of them.&lt;/p&gt;
&lt;p&gt;The two-second failure was trivial. The day was spent on state.&lt;/p&gt;</content:encoded></item><item><title>VCF Operations for Networks 9.1 Search Queries: The vRNI Query Language Lives On</title><link>https://cosmin.us/vcf-operations-for-networks-9-1-search-queries/</link><pubDate>Mon, 27 Jul 2026 07:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/vcf-operations-for-networks-9-1-search-queries/</guid><description>&lt;p&gt;If you spent years typing &lt;code&gt;flows where firewall action = deny&lt;/code&gt; into vRealize Network Insight, then watched the product get renamed to Aria Operations for Networks and then absorbed into VCF Operations as &lt;strong&gt;VCF Operations for Networks&lt;/strong&gt;, you probably have one practical question: did the search language survive?&lt;/p&gt;
&lt;p&gt;It did — completely. I compared the VCF Operations for Networks 9.1 search documentation against the legacy Aria Operations for Networks 6.13 docs page by page, and the query grammar is identical: same entity types, same &lt;code&gt;where&lt;/code&gt; operators, same &lt;code&gt;group by&lt;/code&gt; / &lt;code&gt;order by&lt;/code&gt; / &lt;code&gt;limit&lt;/code&gt; clauses, same planning and path queries. Even the REST API still answers under the old &lt;code&gt;/api/ni/&lt;/code&gt; (Network Insight) namespace. Your saved searches and your muscle memory carry over as-is.&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you spent years typing &lt;code&gt;flows where firewall action = deny&lt;/code&gt; into vRealize Network Insight, then watched the product get renamed to Aria Operations for Networks and then absorbed into VCF Operations as &lt;strong&gt;VCF Operations for Networks&lt;/strong&gt;, you probably have one practical question: did the search language survive?&lt;/p&gt;
&lt;p&gt;It did — completely. I compared the VCF Operations for Networks 9.1 search documentation against the legacy Aria Operations for Networks 6.13 docs page by page, and the query grammar is identical: same entity types, same &lt;code&gt;where&lt;/code&gt; operators, same &lt;code&gt;group by&lt;/code&gt; / &lt;code&gt;order by&lt;/code&gt; / &lt;code&gt;limit&lt;/code&gt; clauses, same planning and path queries. Even the REST API still answers under the old &lt;code&gt;/api/ni/&lt;/code&gt; (Network Insight) namespace. Your saved searches and your muscle memory carry over as-is.&lt;/p&gt;
&lt;p&gt;The short version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VCF Operations 9.1 has &lt;strong&gt;one console but at least four separate query languages&lt;/strong&gt;: the networks entity search (vRNI heritage), the Enhanced Search metric queries (vROps heritage), log search under Operate &amp;gt; Logs, and the new-in-9.1 PromQL Custom Query for real-time metrics. Knowing which bar you are typing into matters more than any syntax detail.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;networks search DSL is unchanged&lt;/strong&gt; from vRNI/AON: &lt;code&gt;&amp;lt;projection&amp;gt; of &amp;lt;entity&amp;gt; where &amp;lt;condition&amp;gt; group by &amp;lt;property&amp;gt; order by &amp;lt;field&amp;gt; limit N&lt;/code&gt;, plus &lt;code&gt;top N&lt;/code&gt;, &lt;code&gt;in last N days&lt;/code&gt;, path queries (&lt;code&gt;VM 'a' to VM 'b'&lt;/code&gt;), and &lt;code&gt;plan&lt;/code&gt; queries for micro-segmentation.&lt;/li&gt;
&lt;li&gt;The 9.1 differences are cosmetic or peripheral: NSX Advanced Load Balancer is renamed &lt;strong&gt;Avi Load Balancer&lt;/strong&gt; in the docs, &amp;ldquo;VMware Cloud (VMC)&amp;rdquo; prose became &amp;ldquo;VMware Cloud on AWS&amp;rdquo;, the VMware SD-WAN (VeloCloud) search-queries page is gone from the 9.1 doc tree, and &lt;strong&gt;AWS/Azure data sources are deprecated&lt;/strong&gt; in 9.1.&lt;/li&gt;
&lt;li&gt;The classic community query lists — the ones we all bookmarked in the vRNI days — still apply. I have collected the best of them below, together with the official examples, into one cookbook.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything here is based on the 9.1 documentation set and verified against the legacy 6.13 docs; sources are linked at the end.&lt;/p&gt;
&lt;h2 id="one-console-four-query-languages"&gt;One Console, Four Query Languages&lt;/h2&gt;
&lt;p&gt;The single most confusing thing about search in VCF Operations 9.1 is that &amp;ldquo;search&amp;rdquo; is not one feature. The console consolidates products that each brought their own query engine, and none of them were merged:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Query surface&lt;/th&gt;
					&lt;th&gt;Where it lives in 9.1&lt;/th&gt;
					&lt;th&gt;Grammar&lt;/th&gt;
					&lt;th&gt;Lineage&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Networks entity search&lt;/td&gt;
					&lt;td&gt;Inside the VCF Operations for Networks section (Operate pillar)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;entity where ... group by ...&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;vRNI / Aria Operations for Networks&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Enhanced Search (metric queries)&lt;/td&gt;
					&lt;td&gt;The global search bar in the top menu, on every page&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Metric of Object Type where ... childOf ...&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;vROps / Aria Operations&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Log search&lt;/td&gt;
					&lt;td&gt;Operate &amp;gt; Logs&lt;/td&gt;
					&lt;td&gt;Log filtering, alert queries, field extraction&lt;/td&gt;
					&lt;td&gt;Aria Operations for Logs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Custom Query (new in 9.1)&lt;/td&gt;
					&lt;td&gt;Operate &amp;gt; Real-Time Metrics / Troubleshooting Workbench&lt;/td&gt;
					&lt;td&gt;PromQL&lt;/td&gt;
					&lt;td&gt;New real-time metrics pipeline&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This post is mainly about the first one, because that is where the network troubleshooting value is. The second gets its own section further down, since the two are easy to conflate — the same word &lt;code&gt;where&lt;/code&gt; appears in both grammars but the operator sets are different. The 9.1 release notes only call out one genuinely new query mechanism, and it is the fourth: &amp;ldquo;Use PromQL-based search queries to derive operational insights.&amp;rdquo; Neither the networks DSL nor the Enhanced Search grammar changed in 9.1.&lt;/p&gt;
&lt;p&gt;A note on architecture, since the rebrand hides it: VCF Operations for Networks is still a &lt;strong&gt;separate, pre-hardened appliance&lt;/strong&gt;, deployed as a Day-2 operation from VCF Operations and lifecycle-managed by Fleet Management — not code merged into the core VCF Operations binary. Only one instance can be integrated per fleet. Licensing is automatic starting with VCF 9: the component is licensed through the vCenter instance it is connected to, and there is nothing to license manually anymore.&lt;/p&gt;
&lt;h2 id="anatomy-of-a-networks-search-query"&gt;Anatomy of a Networks Search Query&lt;/h2&gt;
&lt;p&gt;The documentation splits queries into four categories: structured queries, entity queries, planning queries, and path queries. In practice you will spend most of your time on structured queries, which read like a relaxed SQL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sum(bytes) of flows where Flow Type = &amp;#39;Routed&amp;#39; group by Source Subnet Network order by sum(bytes)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The pieces, in the order the parser expects them:&lt;/p&gt;
&lt;h3 id="entity-type-mandatory"&gt;Entity Type (Mandatory)&lt;/h3&gt;
&lt;p&gt;Every structured query is anchored on an entity type: &lt;code&gt;vms&lt;/code&gt;, &lt;code&gt;hosts&lt;/code&gt;, &lt;code&gt;flows&lt;/code&gt;, &lt;code&gt;firewall rules&lt;/code&gt;, &lt;code&gt;switch ports&lt;/code&gt;, &lt;code&gt;routes&lt;/code&gt;, &lt;code&gt;nsx managers&lt;/code&gt;, &lt;code&gt;kubernetes pods&lt;/code&gt;, and many more. Singular and plural both work, and synonyms are accepted (&lt;code&gt;virtual machine&lt;/code&gt; = &lt;code&gt;vm&lt;/code&gt;). Queries are not case-sensitive.&lt;/p&gt;
&lt;p&gt;Typing just the entity type is already a valid query — &lt;code&gt;vms&lt;/code&gt; lists every VM. The search bar validates as you type: a check mark means the query is valid, a cross means it is not. An old vRNI trick from the community lists that is still worth trying: type &lt;code&gt;help&lt;/code&gt; into the search field to bring up the search help.&lt;/p&gt;
&lt;h3 id="filters-where-plus-operators"&gt;Filters: &lt;code&gt;where&lt;/code&gt; Plus Operators&lt;/h3&gt;
&lt;p&gt;Conditions are &lt;code&gt;property, operator, value&lt;/code&gt;, combined with &lt;code&gt;and&lt;/code&gt; / &lt;code&gt;or&lt;/code&gt; and parentheses:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Operator&lt;/th&gt;
					&lt;th&gt;Example&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;=&lt;/code&gt; / &lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;flows where source ip address = '10.16.240.0/24'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;&lt;/code&gt; &lt;code&gt;&amp;gt;=&lt;/code&gt; &lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vms where memory &amp;gt;= 4096 mb&lt;/code&gt;, &lt;code&gt;vms where cpu usage rate &amp;lt; 70%&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;like&lt;/code&gt; / &lt;code&gt;not like&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vms where name like 'app'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;in&lt;/code&gt; / &lt;code&gt;not in&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;flows where port in (22, 23, 80, 443)&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;is set&lt;/code&gt; / &lt;code&gt;is not set&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vms where firewall rule is set&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;matches&lt;/code&gt; / &lt;code&gt;not matches&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vm where name matches '[a-z]vm-delta[0-9]'&lt;/code&gt; (regex)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;grouping &lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;flows where (src tier = 'App' and destination tier = 'DB') OR (destination tier = 'App' and source tier = 'DB')&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The underrated one is the &lt;strong&gt;nested &lt;code&gt;in&lt;/code&gt;&lt;/strong&gt; — a sub-query as the right-hand side:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vm where host in (host where Blade like &amp;#39;sys/chassis-1&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vm where vlan in (vlan of vm where os in (&amp;#39;Microsoft Windows Server 2003&amp;#39;, &amp;#39;Microsoft Windows Server 2008&amp;#39;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second example is a trimmed version of the documented &amp;ldquo;total attack surface&amp;rdquo; query (the full one lists five vulnerable operating systems and ends with &lt;code&gt;group by Vlan&lt;/code&gt;): every VM that shares a VLAN with a VM running a vulnerable OS.&lt;/p&gt;
&lt;h3 id="projections-what-to-show"&gt;Projections: What to Show&lt;/h3&gt;
&lt;p&gt;By default a query returns the entity&amp;rsquo;s standard field set. A projection in front changes that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Property list&lt;/strong&gt;: &lt;code&gt;os of vms&lt;/code&gt;, &lt;code&gt;cpu cores of vms&lt;/code&gt;, or several at once — &lt;code&gt;CPU Usage Rate, Network Rate, mac address, ip, host of vm 'Quality control-VM26'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Count&lt;/strong&gt;: &lt;code&gt;count of vms&lt;/code&gt;, &lt;code&gt;count of vm where operating system like 'Microsoft Windows Server 2008'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;List()&lt;/strong&gt;: fetch one entity while filtering on another — &lt;code&gt;List(host) of vms where memory &amp;lt;= 2gb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aggregates&lt;/strong&gt; (&lt;code&gt;sum&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;avg&lt;/code&gt;): &lt;code&gt;sum(memory) of hosts&lt;/code&gt;, &lt;code&gt;sum(bytes) of flows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Series&lt;/strong&gt; for time-series graphs: &lt;code&gt;series(avg(cpu usage)) of vms where cpu cores = 4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="grouping-ordering-limiting"&gt;Grouping, Ordering, Limiting&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vms group by host
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sum(bytes) of flows group by dest vm order by sum(bytes)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vms order by memory limit 5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Three rules worth memorizing, because they are the usual cause of the red X:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;limit&lt;/code&gt; must be preceded by &lt;code&gt;order by&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;order by&lt;/code&gt; accepts exactly one field; descending is the default (&lt;code&gt;asc&lt;/code&gt; flips it).&lt;/li&gt;
&lt;li&gt;When a &lt;code&gt;group by&lt;/code&gt; query has a projection or an &lt;code&gt;order by&lt;/code&gt;, the projection &lt;strong&gt;must contain an aggregation function&lt;/strong&gt;. &lt;code&gt;sum(bytes) of flows group by dest vm&lt;/code&gt; is valid; &lt;code&gt;bytes of flows group by dest vm&lt;/code&gt; is explicitly called out in the docs as invalid.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="time-top-n-and-in-last-n-days"&gt;Time: &lt;code&gt;top N&lt;/code&gt; and &lt;code&gt;in last N days&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The examples in the official advanced-queries page lean heavily on two inline constructs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;top 7 vm group by name, Vlan order by sum(Total Network Traffic) in last 7 days
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Kubernetes alerts where alert code = &amp;#39;ImagePullBackOff&amp;#39; in last 24 hours
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;New firewall rules in last 24 hours
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is also a UI time control next to the search bar with presets (&amp;ldquo;last 24 hours&amp;rdquo;, &amp;ldquo;last 3 days&amp;rdquo;), an &lt;strong&gt;At&lt;/strong&gt; option for a point in time, and a &lt;strong&gt;Between&lt;/strong&gt; option for a range — useful for re-running the same query against last Tuesday&amp;rsquo;s state, which remains one of this product&amp;rsquo;s superpowers.&lt;/p&gt;
&lt;h3 id="path-and-planning-queries"&gt;Path and Planning Queries&lt;/h3&gt;
&lt;p&gt;Path queries draw the hop-by-hop topology between two endpoints, including the underlay:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VMware VM &amp;#39;VM01&amp;#39; to VMware VM &amp;#39;VM02&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vm &amp;#39;app1-web&amp;#39; to Internet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Planning queries feed the micro-segmentation donut — the flow analysis view you use before writing distributed-firewall rules. The documented forms take a bare argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;plan security in last 7 days
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;plan securitygroup1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;plan host1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The community lists extend these with filters — &lt;code&gt;plan vm where folder = 'Production'&lt;/code&gt; and &lt;code&gt;plan flows where vm = 'app1-web' and traffic type = 'INTERNET_TRAFFIC'&lt;/code&gt; both come from &lt;a href="https://gist.github.com/mtellin/d48e7cfec03173f6c994e7132a6ebb02"&gt;mtellin&amp;rsquo;s gist&lt;/a&gt; rather than the official grammar reference, but they follow the same &lt;code&gt;where&lt;/code&gt; rules as everything else.&lt;/p&gt;
&lt;h3 id="entity-and-free-text-searches"&gt;Entity and Free-Text Searches&lt;/h3&gt;
&lt;p&gt;Plain strings work too: &lt;code&gt;'prod-68-1'&lt;/code&gt; (exact name, quoted), &lt;code&gt;prod&lt;/code&gt; (partial name), &lt;code&gt;vm app1&lt;/code&gt; (entity type plus partial name). And a community favorite from the gist: paste a bare MAC address like &lt;code&gt;00:50:56:a6:c9:6a&lt;/code&gt; into the search bar to find the machine that owns it — a great party trick when someone shows up with nothing but a switch log.&lt;/p&gt;
&lt;h3 id="vcenter-tags"&gt;vCenter Tags&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vm where tag = &amp;#39;Environment:Production&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The syntax is &lt;code&gt;'{category}:{tagname}'&lt;/code&gt;. Tags are also available as a Group-by criterion in flow analysis (up to three at once).&lt;/p&gt;
&lt;h3 id="saved-searches-and-the-widget-trick"&gt;Saved Searches and the Widget Trick&lt;/h3&gt;
&lt;p&gt;The bookmark icon next to the search bar saves any valid query; saved searches are per-user, and six defaults ship with the product (All Flows, Applications, Azure, Kubernetes Dashboard, Top Trends, NSX). Two features worth more attention than they get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Help &amp;gt; Useful Searches&lt;/strong&gt; is a curated, tagged library of pre-built queries — parameterized ones open pre-filled with sample values you swap out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;View Search Query&lt;/strong&gt; on dashboard widgets (VM Details, Host Details, NSX Manager, Flow Insights, and a few others) reveals the query powering the widget, which you can copy into the search bar and modify. This is the fastest way I know to learn the DSL: find a widget that almost shows what you want, steal its query, adjust.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And almost any search can become an alert: run the query, then create a notification from the results page. Threshold queries (&lt;code&gt;flows where firewall action = deny&lt;/code&gt;, packet-drop queries) make surprisingly good custom alerts.&lt;/p&gt;
&lt;h2 id="the-query-cookbook"&gt;The Query Cookbook&lt;/h2&gt;
&lt;p&gt;The queries below are collected from the official 9.1 search-queries reference and the two classic community lists — &lt;a href="https://letstalkvirtual.com/vrealize-network-insight-search-queries/"&gt;Pat&amp;rsquo;s vRNI search queries post&lt;/a&gt; and &lt;a href="https://gist.github.com/mtellin/d48e7cfec03173f6c994e7132a6ebb02"&gt;mtellin&amp;rsquo;s gist&lt;/a&gt; — deduplicated and grouped by what I actually use them for. Since the grammar is unchanged from vRNI, the community-era queries remain valid in 9.1 unless they depend on a data source that is gone (more on that below).&lt;/p&gt;
&lt;h3 id="flows-and-traffic-patterns"&gt;Flows and Traffic Patterns&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;What it answers&lt;/th&gt;
					&lt;th&gt;Query&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Denied flows, environment-wide&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;flows where firewall action = deny&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Internet traffic by source VM&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sum(bytes) of flows where Flow Type = 'Internet' group by src vm&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Top talkers, limit 10&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sum(bytes) of flows group by src vm order by sum(bytes) limit 10&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Top ports by volume&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sum(bytes) of flow group by port order by sum(bytes)&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Sum and average per port&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sum(bytes), avg(bytes) of flows group by port&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Top routed subnet pairs (last 7 days)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;top 7 flow where Flow Type = 'Routed' group by Source Subnet Network, Destination Subnet Network order by sum(Bytes) in last 7 days&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Traffic that never leaves its subnet — micro-segmentation candidates&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;top 7 flow where Flow Type = 'Switched' group by Subnet Network order by sum(Bytes) in last 7 days&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VMs talking outside the country&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;top 7 flow where Destination Country != 'United States' group by Source VM, Destination Country order by sum(Bytes) in last 7 days&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Traffic between two application tiers, either direction&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Flows where (src tier = 'App' and destination Tier = 'DB') OR (destination tier = 'App' and source tier = 'DB')&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Data center interconnect traffic between two sites&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;sum(bytes) of flows where (Dst Manager = 'site2' AND src manager = 'site1') OR (Dst Manager = 'site1' AND src manager = 'site2')&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="nsx-firewall-and-security"&gt;NSX Firewall and Security&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;What it answers&lt;/th&gt;
					&lt;th&gt;Query&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Effective rules between two VMs&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;firewall rules from 'Prod-Midtier-1' to 'Prod-Db-1'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Any/any allow rules — segmentation debt&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;firewall rule where action = allow and service any = true&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Masked (shadowed) rules&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Firewall Rule Masked Alert&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Rules created recently&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;New firewall rules in last 24 hours&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Rules no flow has ever hit&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Firewall rule where flow is not set&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Flow count per rule&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Flow group by firewall rule&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Flows hitting specific rule IDs&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Flow where rule id in (1011, 1012, 1013)&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VMs excluded from the DFW&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vm where firewall status = 'Excluded'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VMs covered by a given rule&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vm where Firewall Rule = 'Prod MidTier to Prod DB - DBService'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Security groups referenced by no rule at all&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Security group where Indirect Incoming Rules is not set and Indirect Outgoing Rules is not set and Direct Incoming Rules is not set and Direct Outgoing Rules is not set&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Rule and group membership churn&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;firewall rule membership change&lt;/code&gt;, &lt;code&gt;security group membership change&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="vm-and-host-inventory"&gt;VM and Host Inventory&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;What it answers&lt;/th&gt;
					&lt;th&gt;Query&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;VM count per OS&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vms group by operating system&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Segments with the most VMs&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vm group by l2 network&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Datastores with the most VMs&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;vm group by datastore&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Hosts per vSphere version / build&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;host group by version&lt;/code&gt;, &lt;code&gt;host group by OS&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Network identity card for every VM&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;l2 network, vlan, ip address, default gateway of vms&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VMs still running vulnerable operating systems&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;count of vm where Operating System like 'Microsoft Windows Server 2003' or Operating System like 'Microsoft Windows Server 2008'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Everything about one VM in a single line&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CPU Usage Rate, Network Rate, Memory Usage Rate, mac address, ip, vxlan, host of vm 'Quality control-VM26'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="physical-network-and-routing"&gt;Physical Network and Routing&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;What it answers&lt;/th&gt;
					&lt;th&gt;Query&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;All switches from one vendor&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;switches where vendor = Cisco&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Switch ports dropping packets, worst first&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;switchport where vendor = 'Cisco' order by total packet drops&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Routes in a VRF&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;routes where vrf = 'Provider Edge 3'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Routes via a given next hop&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;routes where NextHop Router = 'California-Edge'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;BGP sessions that are not up&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;BGP Connection where BGP Status != 'ACTIVE'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;BGP neighbors of a router&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;list(BGP Neighbor) of NSX-T Router 'Router Name'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ECMP-enabled routers on a manager&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;NSX-T Router where ECMP = 'Enabled' and manager = 'NSX-T Manager Name'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="kubernetes-vks--antrea"&gt;Kubernetes (VKS / Antrea)&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;What it answers&lt;/th&gt;
					&lt;th&gt;Query&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Pod distribution per node&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;kubernetes Pods group by Kubernetes Node&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Nodes not in Ready state&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;kubernetes nodes where Ready != 'True'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Pods created in the last hour&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;new kubernetes pod in last 1 hour&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Dropped flows per service&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;flows where firewall action = 'DROP' group by Kubernetes Service&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Cross-namespace compliance check&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;flows from Kubernetes Namespace 'PCI' to Kubernetes Namespace 'Non-PCI'&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This category got more relevant in 9.1: container IPFIX flows from VKS clusters running Antrea now export to the VCF Operations for Networks collector, with pod-to-pod and pod-to-external traffic dashboards.&lt;/p&gt;
&lt;h3 id="dashboard-shortcuts"&gt;Dashboard Shortcuts&lt;/h3&gt;
&lt;p&gt;Not structured queries, but single keywords from the gist that jump straight to built-in views: &lt;code&gt;problem&lt;/code&gt;, &lt;code&gt;change&lt;/code&gt; (recent changes in the environment), &lt;code&gt;topn&lt;/code&gt;, &lt;code&gt;storage&lt;/code&gt;, and &lt;code&gt;pci compliance of VC Manager '192.168.10.10'&lt;/code&gt; (the PCI dashboard — which, per the gist, required an Enterprise license back in the vRNI days).&lt;/p&gt;
&lt;h3 id="a-caveat-on-community-shorthand"&gt;A Caveat on Community Shorthand&lt;/h3&gt;
&lt;p&gt;The community lists include a few compact forms you will not find in the official grammar reference — &lt;code&gt;vm by VLAN&lt;/code&gt;, &lt;code&gt;show VLAN&lt;/code&gt;, &lt;code&gt;vm by network rate&lt;/code&gt;, &lt;code&gt;top 10 vms by max packet drops&lt;/code&gt;, &lt;code&gt;flows where flow type = 'VM-Physical' by bytes&lt;/code&gt;. They come from vRNI-era posts that predate the current documented grammar, which only uses &lt;code&gt;group by&lt;/code&gt; / &lt;code&gt;order by&lt;/code&gt; for these jobs. I have kept them out of the tables above for that reason. If you rely on one of them, the in-product validation check mark will tell you immediately whether your build still accepts it.&lt;/p&gt;
&lt;h2 id="the-other-search-bar-enhanced-search-metric-queries"&gt;The Other Search Bar: Enhanced Search Metric Queries&lt;/h2&gt;
&lt;p&gt;The global search bar at the top of the VCF Operations console — the one that is on every page — is a different animal with its own documented query language, under the section Broadcom titles &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/enhanced-search-capability.html"&gt;&amp;ldquo;Enhanced Search Capability&amp;rdquo;&lt;/a&gt;. It searches the vROps-side world: objects, metrics, properties, dashboards, reports, views, alert definitions, symptom definitions, recommendations, notifications, IP addresses, super metrics, and (since recent releases) features — type &lt;code&gt;li&lt;/code&gt; and select Feature to jump straight to Licenses, navigation path included.&lt;/p&gt;
&lt;p&gt;Its metric query grammar looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CPU|Usage % of Virtual Machine
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CPU|Usage % of Virtual Machine where CPU|Usage % &amp;gt; 15 and Memory|Usage % &amp;gt; 15
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CPU|Demand MHz of Virtual Machine where Configuration|Name contains &amp;#39;Centos&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CPU|Usage % of Virtual Machine where CPU|Usage % &amp;gt; 15 or Memory|Usage % &amp;gt; 15 childOf vSphere World
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The differences from the networks DSL are exactly where you would trip:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The pattern is &lt;code&gt;Metric of Object Type&lt;/code&gt;, with the pipe character separating metric group and metric name (&lt;code&gt;CPU|Usage %&lt;/code&gt;). If you start with a metric name, naming the object type is mandatory.&lt;/li&gt;
&lt;li&gt;String operators are camelCase keywords: &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;notEquals&lt;/code&gt;, &lt;code&gt;contains&lt;/code&gt;, &lt;code&gt;notContains&lt;/code&gt;, &lt;code&gt;startsWith&lt;/code&gt;, &lt;code&gt;notStartsWith&lt;/code&gt; — there is no &lt;code&gt;like&lt;/code&gt;, no &lt;code&gt;in&lt;/code&gt;, no &lt;code&gt;matches&lt;/code&gt; here. Numeric operators (&lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;) plus arithmetic (&lt;code&gt;+ - * /&lt;/code&gt; with parentheses) and &lt;code&gt;and&lt;/code&gt;/&lt;code&gt;or&lt;/code&gt; round out the set. String values are case-insensitive.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;childOf&lt;/code&gt; at the end of the query scopes results to descendants of an ancestor object — the closest thing this grammar has to the networks DSL&amp;rsquo;s nested &lt;code&gt;in&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Hard limits: 300 characters per query, at most five different metric names. Break either and a red banner explains the issue; for syntax errors specifically it reads &amp;ldquo;The query contains syntax error(s). Please modify it to get suggestions.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;There is no &lt;code&gt;group by&lt;/code&gt; or &lt;code&gt;order by&lt;/code&gt; in the query itself; slicing happens on the Metric Search Results page instead, via Transformation (Min/Max/Average/Sum/First/Last/Current), Roll Up (hour through year), Parents, value-distribution buckets, and time settings. The Transformation filter only activates when the query has a &lt;code&gt;where&lt;/code&gt; condition and the object type has at most 200 objects over at most one month. From any result row, the vertical-ellipsis menu jumps into the Troubleshooting Workbench.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One honest note: none of this is new in 9.1. I compared the 9.0 and 9.1 pages — the content is identical — and the same grammar exists in Aria Operations 8.x. What is new in 9.1 on the metrics side is the PromQL-based Custom Query in Real-Time Metrics, which is a separate topic (and a separate language) altogether.&lt;/p&gt;
&lt;h2 id="what-actually-changed-in-91-for-operations-for-networks"&gt;What Actually Changed in 9.1 for Operations for Networks&lt;/h2&gt;
&lt;p&gt;Since search itself did not move, here is what did — the 9.1 items relevant to a networks operator, from the release notes and product support notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Network Assessment and Value report&lt;/strong&gt; — evaluates your current VDS-based networking and produces a CAPEX/OPEX analysis for VCF networking capabilities. It replaces the vCenter Assessment report, which was removed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VPC Planning&lt;/strong&gt; — guided vSphere-to-VPC transition planning, with subnet tables and a CSV export of the generated VPC design.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expanded IPFIX for VKS clusters&lt;/strong&gt; — Antrea container flows land in the collector; pod-to-pod and pod-to-external dashboards.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expanded migration planning&lt;/strong&gt; — automatic migration wave generation, clustering workloads by flow, latency, and bandwidth data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network health dashboards&lt;/strong&gt; — NSX Edge appliance and ESX host networking capacity monitoring surfaced through VCF Operations Health and Diagnostics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deprecations&lt;/strong&gt;: AWS and Azure data sources are deprecated in 9.1, and the networks API had 22 operations marked deprecated — the largest deprecation set in the Operations component. The VMware SD-WAN (VeloCloud) search-queries reference page is also absent from the 9.1 documentation tree.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That deprecation list is worth acting on if you have search-based alerting or scripts built on AWS/Azure entity queries (&lt;code&gt;AWS EC2 where Security Group = ...&lt;/code&gt;, &lt;code&gt;Azure NSG Rule where Action = 'ALLOW'&lt;/code&gt;) — the syntax still exists in the 9.1 reference today, but the data sources feeding those entities are on the way out.&lt;/p&gt;
&lt;h2 id="getting-there-from-standalone-6x"&gt;Getting There from Standalone 6.x&lt;/h2&gt;
&lt;p&gt;If you are still on a standalone appliance — I have covered &lt;a href="https://cosmin.us/upgrading-aria-operations-for-networks-to-6-14-via-upgrade-bundle/"&gt;upgrading Aria Operations for Networks to 6.14 via the upgrade bundle&lt;/a&gt; and &lt;a href="https://cosmin.us/upgrading-aria-operations-for-networks-to-6-14-via-aria-suite-lifecycle/"&gt;via Aria Suite Lifecycle&lt;/a&gt; before — the path into 9.x is: get to &lt;strong&gt;6.14.0 Patch 6 or 6.14.1+&lt;/strong&gt;, import the instance into VCF Operations Fleet Management, upgrade it in place to VCF Operations for Networks 9.0.x, and from there directly to 9.1. Only one instance can be imported per fleet. Every search query in this post works the same on both sides of that migration, which is precisely the point.&lt;/p&gt;
&lt;h2 id="the-api-corner"&gt;The API Corner&lt;/h2&gt;
&lt;p&gt;For automation, the search subsystem is exposed over REST, still under the Network Insight namespace:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /api/ni/search/ql&lt;/code&gt; — takes the same query-language strings you type in the search bar&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /api/ni/search&lt;/code&gt; — entity search&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /api/ni/search/aggregation&lt;/code&gt; — aggregations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /api/ni/search/groupby&lt;/code&gt; — group-by queries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The schemas are on the &lt;a href="https://developer.broadcom.com/xapis/vcf-operations-for-networks-api/latest/"&gt;VCF Operations for Networks API reference&lt;/a&gt;. The &lt;code&gt;ql&lt;/code&gt; endpoint is the interesting one: anything you prototype in the search bar can be lifted verbatim into a script.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/network-operationss/search/search-queries.html"&gt;Broadcom TechDocs — Working with Search Queries (VCF Operations for Networks 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/enhanced-search-capability.html"&gt;Broadcom TechDocs — Enhanced Search Capability (VCF Operations 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/enhanced-search-capability/searching-for-metrics-properties-or-object-types-using-queries.html"&gt;Broadcom TechDocs — Searching for Metrics, Properties, or Object Types Using Queries (VCF Operations 9.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/aria/aria-operations-for-networks/6-13/vrealize-network-insight-ug-4-1-and-later-6-13/search/search-queries.html"&gt;Broadcom TechDocs — Search Queries (Aria Operations for Networks 6.13, the legacy twin of the 9.1 pages)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/what-s-new/whats-new-vcf-ops.html"&gt;Broadcom TechDocs — What&amp;rsquo;s New in VCF Operations (VCF 9.1 Release Notes)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/vmware-cloud-foundation-9-1-0-0-release-notes/vcf-91-product-support-notes.html"&gt;Broadcom TechDocs — VCF 9.1 Product Support Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://letstalkvirtual.com/vrealize-network-insight-search-queries/"&gt;letstalkvirtual.com — vRealize Network Insight Search Queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/mtellin/d48e7cfec03173f6c994e7132a6ebb02"&gt;GitHub gist (mtellin) — vRealize Network Insight Search Queries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Fixing an NSX Service Account Rotation Loop in VCF 9.1: PASSWORD_MANAGER_NSX_USER_API_FAILED and the 128-Character Password</title><link>https://cosmin.us/fixing-nsx-service-account-rotation-loop-password-manager-nsx-user-api-failed-vcf-9-1/</link><pubDate>Wed, 15 Jul 2026 17:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/fixing-nsx-service-account-rotation-loop-password-manager-nsx-user-api-failed-vcf-9-1/</guid><description>&lt;p&gt;Continuing the credential cleanup in the &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;same 9.1 environment&lt;/a&gt; from the &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;expired password sync&lt;/a&gt; and &lt;a href="https://cosmin.us/fixing-vcfopspwid03-call-from-nsx-received-invalid-response/"&gt;VCFOPSPWID03&lt;/a&gt; posts, one credential refused to settle: the service account SDDC Manager uses to talk to the NSX Manager API, &lt;code&gt;svc-sddcmanager-a-nsx-mgmt--8199&lt;/code&gt; in my lab. It showed up as &lt;strong&gt;disconnected&lt;/strong&gt;, every remediation attempt failed, and the environment kept trying to rotate it in a loop.&lt;/p&gt;
&lt;p&gt;This one took several wrong turns before the actual root cause surfaced, and almost every wrong turn taught me something worth writing down. The short version:&lt;/p&gt;</description><content:encoded>&lt;p&gt;Continuing the credential cleanup in the &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;same 9.1 environment&lt;/a&gt; from the &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;expired password sync&lt;/a&gt; and &lt;a href="https://cosmin.us/fixing-vcfopspwid03-call-from-nsx-received-invalid-response/"&gt;VCFOPSPWID03&lt;/a&gt; posts, one credential refused to settle: the service account SDDC Manager uses to talk to the NSX Manager API, &lt;code&gt;svc-sddcmanager-a-nsx-mgmt--8199&lt;/code&gt; in my lab. It showed up as &lt;strong&gt;disconnected&lt;/strong&gt;, every remediation attempt failed, and the environment kept trying to rotate it in a loop.&lt;/p&gt;
&lt;p&gt;This one took several wrong turns before the actual root cause surfaced, and almost every wrong turn taught me something worth writing down. The short version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SDDC Manager generated a &lt;strong&gt;128-character&lt;/strong&gt; password for the service account, but its own remediate path enforces a &lt;strong&gt;127-character&lt;/strong&gt; maximum — an off-by-one between the generator and the validator that keeps the rotation failing.&lt;/li&gt;
&lt;li&gt;The service account is an &lt;strong&gt;OS-level PAM user&lt;/strong&gt; on the NSX appliance: invisible to the NSX API, UI, and CLI, manageable only as &lt;code&gt;root&lt;/code&gt; with standard Linux tools.&lt;/li&gt;
&lt;li&gt;Failed rotation attempts had &lt;strong&gt;locked the account&lt;/strong&gt; at the NSX API level.&lt;/li&gt;
&lt;li&gt;And the final boss: SDDC Manager&amp;rsquo;s rotation machinery was &lt;strong&gt;re-changing the password within a couple of seconds&lt;/strong&gt; of any manual reset, so every fix I tested was already stale by the time I tested it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-symptom-a-service-account-stuck-disconnected"&gt;The Symptom: A Service Account Stuck Disconnected&lt;/h2&gt;
&lt;p&gt;The credential in question, as SDDC Manager tracks it:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Username&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;svc-sddcmanager-a-nsx-mgmt--8199&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Resource type&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;NSXT_MANAGER&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Credential type&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;API&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Account type&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SERVICE&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The rotation failures are visible on the SDDC Manager appliance in the operations manager log:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -f /var/log/vmware/vcf/operationsmanager/operationsmanager.log &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;svc-sddcmanager|ROTATE|REMEDIATE|diagnosticMessage&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The recurring entry that matters (flattened here):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;errorCode: PASSWORD_MANAGER_NSX_USER_API_FAILED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;diagnosticMessage: Response from NSX &amp;lt;nsx-manager-fqdn&amp;gt;: 403 -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;The credentials were incorrect or the account specified has been locked.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A 403 for a service account that only SDDC Manager itself manages. Nobody had touched this password by hand — SDDC Manager rotated it, and then could no longer authenticate with the value it had just set.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The examples in this post use &lt;code&gt;curl -k&lt;/code&gt;, which skips TLS certificate verification — fine in a lab where the appliances still run self-signed certificates. In production, drop &lt;code&gt;-k&lt;/code&gt; and trust the appliance CA instead. If any password you type contains &lt;code&gt;!&lt;/code&gt;, disable history expansion first (&lt;code&gt;set +H&lt;/code&gt; in bash, &lt;code&gt;unsetopt BANG_HIST&lt;/code&gt; in zsh) so the shell does not mangle it. And the &lt;code&gt;read -rp&lt;/code&gt; / &lt;code&gt;read -rsp&lt;/code&gt; prompts use bash syntax — on the appliances that is the shell you get, but if you run any of this locally from zsh, start &lt;code&gt;bash&lt;/code&gt; first or use the zsh form (&lt;code&gt;read -rs 'VAR?prompt: '&lt;/code&gt;); in zsh, &lt;code&gt;read -rsp&lt;/code&gt; silently leaves the variable empty, and an empty password produces confusing downstream errors instead of a clear failure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="the-off-by-one-128-generated-vs-127-accepted"&gt;The Off-by-One: 128 Generated vs 127 Accepted&lt;/h2&gt;
&lt;p&gt;Digging through the failed rotation tasks showed the first real clue: the password SDDC Manager had generated for the service account was &lt;strong&gt;128 characters&lt;/strong&gt; long, while the remediate path rejects anything over &lt;strong&gt;127 characters&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The generator takes its cue from the NSX node authentication policy. NSX advertises a &lt;code&gt;maximum_password_length&lt;/code&gt; of 128 by default, SDDC Manager generates a password at that maximum, and then its own validation refuses to remediate with it. The account ends up in the worst possible state: the rotation half-succeeds often enough to keep changing the password, but remediation can never complete, so the credential is marked disconnected and the platform keeps retrying.&lt;/p&gt;
&lt;h2 id="dead-ends-worth-knowing-about"&gt;Dead Ends Worth Knowing About&lt;/h2&gt;
&lt;p&gt;Before the fix, the detours — each one reasonable, each one wrong.&lt;/p&gt;
&lt;h3 id="remediating-from-the-ui"&gt;Remediating from the UI&lt;/h3&gt;
&lt;p&gt;The obvious first move. The remediation workflow failed with the same 403, and the UI adds nothing to the diagnosis — the useful error text only exists in &lt;code&gt;operationsmanager.log&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="guessing-the-credential-tuple"&gt;Guessing the Credential Tuple&lt;/h3&gt;
&lt;p&gt;My first API attempts failed validation before doing anything, because a &lt;code&gt;PATCH /v1/credentials&lt;/code&gt; call must reference the &lt;strong&gt;exact tuple&lt;/strong&gt; SDDC Manager has on file: &lt;code&gt;resourceName&lt;/code&gt;, &lt;code&gt;resourceType&lt;/code&gt;, &lt;code&gt;credentialType&lt;/code&gt;, and &lt;code&gt;username&lt;/code&gt; all have to match. Do not guess any of them — read them back first.&lt;/p&gt;
&lt;p&gt;Get a token (I ran these directly on the SDDC Manager appliance against &lt;code&gt;localhost&lt;/code&gt;, but the FQDN works the same):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SDDC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://localhost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rp &lt;span class="s2"&gt;&amp;#34;SSO username: &amp;#34;&lt;/span&gt; SSO_USER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;SSO password: &amp;#34;&lt;/span&gt; SSO_PASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;jq -nc --arg u &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SSO_USER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --arg p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SSO_PASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{username:$u,password:$p}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -sk -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/tokens&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq -r .accessToken&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; SSO_PASS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;null&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Token acquisition failed&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The block prints nothing on success — the token lands silently in &lt;code&gt;$TOKEN&lt;/code&gt;. Test it with a cheap authenticated call before building anything on top of it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -o /dev/null -w &lt;span class="s2"&gt;&amp;#34;%{http_code}\n&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/domains&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A &lt;code&gt;200&lt;/code&gt; means the token works. Anything else, start over at the token block.&lt;/p&gt;
&lt;p&gt;List the NSX credentials and pull the exact tuple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/credentials?resourceType=NSXT_MANAGER&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.elements[] | select(.accountType==&amp;#34;SERVICE&amp;#34;) |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; [.resource.resourceName, .resource.resourceType, .credentialType, .username] | @tsv&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That returns the values used in the remediation calls later in this post.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;jq&lt;/code&gt; fails here with &lt;em&gt;&amp;ldquo;Cannot iterate over null&amp;rdquo;&lt;/em&gt;, the response was not a credentials list — it is almost always an authentication error object instead. Check the HTTP status and the raw body:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -o /tmp/creds.json -w &lt;span class="s2"&gt;&amp;#34;%{http_code}\n&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/credentials?resourceType=NSXT_MANAGER&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq . /tmp/creds.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A &lt;code&gt;401&lt;/code&gt; means the token is empty, expired (they are short-lived), or the literal string &lt;code&gt;null&lt;/code&gt; from a failed acquisition — re-run the token block above and mind its failure check. A &lt;code&gt;401&lt;/code&gt; body complaining that &lt;em&gt;&amp;ldquo;jwt strings must contain exactly 2 period characters&amp;rdquo;&lt;/em&gt; is the same problem stated more directly: whatever is in &lt;code&gt;$TOKEN&lt;/code&gt; is not a JWT at all, and the usual cause is a rejected &lt;code&gt;/v1/tokens&lt;/code&gt; login quietly turning into &lt;code&gt;null&lt;/code&gt; — check that the username is a full SSO identity such as &lt;code&gt;administrator@vsphere.local&lt;/code&gt;, not a bare username.&lt;/p&gt;
&lt;p&gt;Capture the service account username while you are here — the remediation and rotation calls later run in this same SDDC Manager session and reuse it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SVC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -sk -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/credentials?resourceType=NSXT_MANAGER&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.elements[] | select(.accountType==&amp;#34;SERVICE&amp;#34;) | .username&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you spend time in this API, the &lt;a href="https://cosmin.us/mastering-the-v1-credentials-api-in-sddc-manager-for-enhanced-security-management/"&gt;/v1/credentials overview&lt;/a&gt; covers the basic operations.&lt;/p&gt;
&lt;h3 id="changing-the-vcf-operations-password-policies"&gt;Changing the VCF Operations Password Policies&lt;/h3&gt;
&lt;p&gt;VCF Operations 9.1 exposes password policy settings, and lowering a maximum length there looked like the clean fix for the 128-character problem. It did nothing. Those policies do &lt;strong&gt;not&lt;/strong&gt; govern what SDDC Manager generates for service accounts — the generator follows the NSX node authentication policy, not VCF Operations. Worth knowing before you spend an afternoon proving it.&lt;/p&gt;
&lt;h2 id="the-first-fix-lower-the-nsx-maximum-password-length"&gt;The First Fix: Lower the NSX Maximum Password Length&lt;/h2&gt;
&lt;p&gt;Since the generator follows the NSX policy maximum and the validator caps at 127, the workaround is to lower the NSX &lt;code&gt;maximum_password_length&lt;/code&gt; so generated passwords land under the limit. I went to 100 — comfortably below 127, still far longer than any human-managed password.&lt;/p&gt;
&lt;p&gt;One trap first: the setting lives in the &lt;strong&gt;auth-policy&lt;/strong&gt; endpoint. There is no &lt;code&gt;/api/v1/node/aaa/password-complexity&lt;/code&gt; endpoint — that name belongs to the NSX CLI command (&lt;code&gt;get&lt;/code&gt;/&lt;code&gt;set password-complexity&lt;/code&gt;), and the API returns 404 for it. The REST path you want is &lt;code&gt;/api/v1/node/aaa/auth-policy&lt;/code&gt;, the same endpoint from the &lt;a href="https://cosmin.us/fixing-vcfopspwid03-call-from-nsx-received-invalid-response/"&gt;password reuse post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Back up the current policy before changing anything:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;NSX admin password: &amp;#34;&lt;/span&gt; NSXPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tee /tmp/auth-policy-backup.json &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api_failed_auth_lockout_period&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api_max_auth_failures&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;minimum_password_length&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;maximum_password_length&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lower the maximum (&lt;code&gt;maximum_password_length&lt;/code&gt; accepts 8 to 128):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;maximum_password_length&amp;#34;: 100}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Policy changes take a short while to apply and synchronize across the manager cluster nodes in 9.x — read the value back before moving on:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="p"&gt;|&lt;/span&gt; jq .maximum_password_length
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;New rotations now generate passwords of at most 100 characters, comfortably under the 127-character validation. Problem solved — except the account was still disconnected, and remediation still failed with the same 403.&lt;/p&gt;
&lt;h2 id="plot-twist-test_before_remediate"&gt;Plot Twist: TEST_BEFORE_REMEDIATE&lt;/h2&gt;
&lt;p&gt;The remediation task logs showed the failure happening in a &lt;code&gt;TEST_BEFORE_REMEDIATE&lt;/code&gt; stage. Before SDDC Manager remediates a credential, it first &lt;strong&gt;authenticates against NSX as the service account&lt;/strong&gt; with the password it believes is current. If that test fails — wrong password &lt;em&gt;or&lt;/em&gt; locked account — the whole remediation aborts with &lt;code&gt;PASSWORD_MANAGER_NSX_USER_API_FAILED&lt;/code&gt;, and nothing is remediated.&lt;/p&gt;
&lt;p&gt;That explains the loop: the earlier failed rotations had burned through NSX&amp;rsquo;s API authentication failure allowance (&lt;code&gt;api_max_auth_failures: 5&lt;/code&gt; in the policy above), the account got locked, and from then on even a correct password could not pass the pre-remediation test.&lt;/p&gt;
&lt;p&gt;So before anything else could work, I had to look at the account itself on the NSX side — which is where this got interesting.&lt;/p&gt;
&lt;h2 id="the-hidden-user-an-api-only-pam-account"&gt;The Hidden User: An API-Only PAM Account&lt;/h2&gt;
&lt;p&gt;The service account does not exist as far as the NSX management plane is concerned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /api/v1/node/users&lt;/code&gt; — not listed.&lt;/li&gt;
&lt;li&gt;NSX UI under user management — not there.&lt;/li&gt;
&lt;li&gt;NSX CLI &lt;code&gt;get user&lt;/code&gt; / &lt;code&gt;set user&lt;/code&gt; — unknown user.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But on the NSX Manager appliance as &lt;code&gt;root&lt;/code&gt;, it is a perfectly ordinary Linux account:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep svc- /etc/passwd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;svc-sddcmanager-a-nsx-mgmt--8199:x:...:...:/home/svc-sddcmanager-a-nsx-mgmt--8199:...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The account authenticates to the NSX API through PAM, has password history in &lt;code&gt;/etc/security/opasswd&lt;/code&gt;, and is managed by exactly nothing except &lt;code&gt;root&lt;/code&gt; and the standard Linux toolbox. No SSH access, no UI, API-only. That means the diagnostics you would normally run through the NSX API happen with &lt;code&gt;faillock&lt;/code&gt; and &lt;code&gt;chage&lt;/code&gt; instead:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SVC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;svc-sddcmanager-a-nsx-mgmt--8199&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;faillock --user &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chage -l &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;faillock&lt;/code&gt; confirmed the lockout — a stack of recent authentication failures, courtesy of the rotation loop. Clear it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;faillock --user &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --reset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While in there, two more root-level tools are worth knowing for this account type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;passwd &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# set a known password, as root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &amp;gt; /etc/security/opasswd &lt;span class="c1"&gt;# wipe PAM password history — see warning&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;code&gt;/etc/security/opasswd&lt;/code&gt; holds the PAM password history for &lt;strong&gt;every&lt;/strong&gt; user on the appliance, not just the service account. Emptying it is a blunt lab move to get past &amp;ldquo;password has been already used&amp;rdquo; rejections during repeated testing. Do not do this on a production appliance without a support ticket telling you to.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="keeping-the-account-from-locking-again"&gt;Keeping the Account from Locking Again&lt;/h3&gt;
&lt;p&gt;The lockout pattern here matches &lt;a href="https://knowledge.broadcom.com/external/article?articleNumber=420736"&gt;Broadcom KB 420736&lt;/a&gt;: SDDC Manager trips the NSX API lockout with its own failed authentication attempts, and remediation can never win against a locked account. The durable fix is to add SDDC Manager&amp;rsquo;s IP to the NSX &lt;code&gt;lockout_immune_addresses&lt;/code&gt; list, so its authentication failures never lock anything.&lt;/p&gt;
&lt;p&gt;Run this &lt;strong&gt;on the SDDC Manager appliance&lt;/strong&gt;, so &lt;code&gt;hostname -i&lt;/code&gt; resolves to the right IP; the jq filter appends it to the existing list without clobbering other entries:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;PUT /api/v1/cluster/api-service&lt;/code&gt; replaces the entire cluster-wide API service configuration document, and NSX restarts the API service to apply it — expect a brief API interruption, so run this when nothing else is mid-call. The pipeline below reads the full current document and keeps a copy in &lt;code&gt;/tmp/api-service-backup.json&lt;/code&gt; before anything is overwritten.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/cluster/api-service &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tee /tmp/api-service-backup.json &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; jq --arg ip &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;hostname -i&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;.lockout_immune_addresses += [$ip] | .lockout_immune_addresses |= unique&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -sk -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/cluster/api-service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the lockout cleared and immunity in place, I reset the service account password as &lt;code&gt;root&lt;/code&gt;, confirmed it worked with a quick authentication test&amp;hellip;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;Service account password you just set: &amp;#34;&lt;/span&gt; SVCPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -o /dev/null -w &lt;span class="s2"&gt;&amp;#34;%{http_code}\n&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -u &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$SVCPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/cluster/status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; SVCPASS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;and got &lt;code&gt;403&lt;/code&gt;. Seconds after setting it. With a password I had just set moments earlier.&lt;/p&gt;
&lt;h2 id="the-real-root-cause-racing-the-rotation-engine"&gt;The Real Root Cause: Racing the Rotation Engine&lt;/h2&gt;
&lt;p&gt;This was the point where the investigation stopped being about policies and started being about timing. The sequence kept repeating:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As &lt;code&gt;root&lt;/code&gt; on NSX: &lt;code&gt;passwd&lt;/code&gt; the service account to a known value.&lt;/li&gt;
&lt;li&gt;Test authentication with that value: &lt;code&gt;200&lt;/code&gt; — for a moment.&lt;/li&gt;
&lt;li&gt;Test again a few seconds later: &lt;code&gt;403&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Nothing was &amp;ldquo;wrong&amp;rdquo; with any password. &lt;strong&gt;SDDC Manager&amp;rsquo;s rotation machinery was re-changing the service account password within a couple of seconds of any reset.&lt;/strong&gt; The disconnected credential kept the retry loop hot, every retry rotated the password on the appliance, and every manual test raced the engine and lost. The vault was not stale, the policy was not blocking anything anymore — my known-good password simply stopped being the current password almost immediately after I set it.&lt;/p&gt;
&lt;p&gt;Watching the log in one terminal makes the race visible:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -f /var/log/vmware/vcf/operationsmanager/operationsmanager.log &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;svc-sddcmanager|ROTATE|REMEDIATE&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The fix is to stop fighting the engine and synchronize with it instead: set the password and hand it to SDDC Manager &lt;strong&gt;inside that couple-second window&lt;/strong&gt;, so the value on the appliance and the value being remediated converge before the next rotation fires.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ What follows is a lab maneuver. Manually resetting a platform-managed service account and racing the rotation engine is recoverable here; on a production NSX Manager, capture &lt;code&gt;operationsmanager.log&lt;/code&gt;, the failed task IDs, and the &lt;code&gt;faillock&lt;/code&gt; output and open a support request first — see the last bullet in Hardening Takeaways.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In practice, that means staging everything in advance:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Terminal 1 — NSX Manager as root&lt;/strong&gt;, with a non-interactive password set ready to run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;New service account password: &amp;#34;&lt;/span&gt; SVCPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$SVCPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; chpasswd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; SVCPASS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Terminal 2 — the same SDDC Manager session from earlier&lt;/strong&gt;, so &lt;code&gt;$SDDC&lt;/code&gt;, &lt;code&gt;$TOKEN&lt;/code&gt;, and &lt;code&gt;$SVC&lt;/code&gt; are still set, with the remediation payload pre-built — the only thing left is to press Enter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;Same password as set on NSX: &amp;#34;&lt;/span&gt; SVCPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TASK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;jq -nc &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg fqdn &lt;span class="s2"&gt;&amp;#34;&amp;lt;nsx-manager-fqdn&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg user &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg pw &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVCPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{operationType:&amp;#34;REMEDIATE&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; elements:[{resourceName:$fqdn, resourceType:&amp;#34;NSXT_MANAGER&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; credentials:[{credentialType:&amp;#34;API&amp;#34;, username:$user, password:$pw}]}]}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -sk -X PATCH &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/credentials&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq -r .id&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; SVCPASS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run terminal 1, then terminal 2 immediately — within the window, before the engine rotates again. Poll the task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/credentials/tasks/&lt;/span&gt;&lt;span class="nv"&gt;$TASK_ID&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;{name, status}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This time &lt;code&gt;TEST_BEFORE_REMEDIATE&lt;/code&gt; authenticated successfully — the password it tested was, at that moment, actually the password on the appliance — and the remediation completed. The credential flipped back to healthy, and the rotation loop stopped, because there was nothing left to retry.&lt;/p&gt;
&lt;p&gt;To finish cleanly, trigger one supervised rotation so the account ends up on a fresh SDDC Manager-generated password (now capped at 100 characters by the lowered policy) instead of a hand-typed one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq -nc &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg fqdn &lt;span class="s2"&gt;&amp;#34;&amp;lt;nsx-manager-fqdn&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg user &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SVC&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{operationType:&amp;#34;ROTATE&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; elements:[{resourceName:$fqdn, resourceType:&amp;#34;NSXT_MANAGER&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; credentials:[{credentialType:&amp;#34;API&amp;#34;, username:$user}]}]}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -X PATCH &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SDDC&lt;/span&gt;&lt;span class="s2"&gt;/v1/credentials&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Poll the task the same way as before; with the loop broken and the policy cap in place, the rotation completes cleanly.&lt;/p&gt;
&lt;h2 id="verification"&gt;Verification&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The rotation task in SDDC Manager completes successfully, and no new &lt;code&gt;ROTATE&lt;/code&gt;/&lt;code&gt;REMEDIATE&lt;/code&gt; retries appear in &lt;code&gt;operationsmanager.log&lt;/code&gt; for the service account.&lt;/li&gt;
&lt;li&gt;The credential status is back to healthy (&lt;code&gt;GET /v1/credentials&lt;/code&gt; shows no error state for the tuple).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;faillock --user &amp;quot;$SVC&amp;quot;&lt;/code&gt; on the NSX appliance stays empty.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lockout_immune_addresses&lt;/code&gt; in &lt;code&gt;GET /api/v1/cluster/api-service&lt;/code&gt; contains the SDDC Manager IP.&lt;/li&gt;
&lt;li&gt;The NSX auth-policy holds the values you intend to keep — in my case &lt;code&gt;maximum_password_length: 100&lt;/code&gt; stays, since the 128/127 mismatch would otherwise come straight back on the next rotation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clean up the working files, since the backups describe your security posture and the lockout-immune IP list:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -f /tmp/auth-policy-backup.json /tmp/api-service-backup.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; NSXPASS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="hardening-takeaways"&gt;Hardening Takeaways&lt;/h2&gt;
&lt;p&gt;A few things I am keeping from this one, beyond the fix itself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Add SDDC Manager to &lt;code&gt;lockout_immune_addresses&lt;/code&gt; proactively.&lt;/strong&gt; Any credential workflow that can fail more times than &lt;code&gt;api_max_auth_failures&lt;/code&gt; allows can lock its own account and wedge remediation. &lt;a href="https://knowledge.broadcom.com/external/article?articleNumber=420736"&gt;KB 420736&lt;/a&gt; describes the pattern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Back up &lt;code&gt;auth-policy&lt;/code&gt; before any PUT&lt;/strong&gt; — &lt;code&gt;tee&lt;/code&gt; to a file costs nothing, and the endpoint carries every knob (lockout periods, complexity, history) in one document, so an overzealous edit has a wide blast radius.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check NTP while you are in the logs.&lt;/strong&gt; This environment&amp;rsquo;s &lt;code&gt;operationsmanager.log&lt;/code&gt; also showed intermittent JWT clock-skew errors between components — harmless-looking noise that can turn into real authentication failures. Time sync issues and credential troubleshooting mix badly; rule them out early.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Know when to stop and open an SR.&lt;/strong&gt; Manually resetting a service account password as &lt;code&gt;root&lt;/code&gt; and racing the rotation engine is recoverable in a lab. In production, the moment a platform-managed service account is in a rotation loop, capture &lt;code&gt;operationsmanager.log&lt;/code&gt;, the failed task IDs, and the &lt;code&gt;faillock&lt;/code&gt; output, and open a support request — &lt;a href="https://knowledge.broadcom.com/external/article?articleNumber=420736"&gt;KB 420736&lt;/a&gt; describes the lockout pattern, and &lt;a href="https://knowledge.broadcom.com/external/article?articleNumber=423038"&gt;KB 423038&lt;/a&gt; is a related reference worth attaching alongside it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="final-notes"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;Every layer of this problem produced the same useless symptom — a 403 — for four different reasons: a password too long to remediate, a locked account, a pre-remediation test that authenticates as the account it is about to fix, and finally a rotation engine overwriting the password faster than I could test it. The 128-versus-127 mismatch set the loop in motion, but the thing that made it unfixable-looking was the timing: every diagnostic I ran was measuring a password that had already been replaced.&lt;/p&gt;
&lt;p&gt;Two habits would have shortened this significantly. First, &lt;code&gt;tail -f&lt;/code&gt; the operations manager log &lt;em&gt;before&lt;/em&gt; forming a theory — the rotation attempts were right there, timestamped, showing the engine acting seconds after every manual change. Second, when a platform manages an account, assume it will keep managing it while you troubleshoot: either pause the machinery or synchronize with it. Fighting an automated rotation engine with manual password resets is a race you lose every time — until you make the race the plan.&lt;/p&gt;</content:encoded></item><item><title>Fixing VCFOPSPWID03: NSX Password Update Failed as Call from NSX Received Invalid Response (VCF 9.1)</title><link>https://cosmin.us/fixing-vcfopspwid03-call-from-nsx-received-invalid-response/</link><pubDate>Sat, 11 Jul 2026 17:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/fixing-vcfopspwid03-call-from-nsx-received-invalid-response/</guid><description>&lt;p&gt;While rotating credentials in the &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;same 9.1 environment&lt;/a&gt; I covered in the &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;expired password sync post&lt;/a&gt;, an &lt;strong&gt;Update&lt;/strong&gt; of the NSX Manager &lt;code&gt;admin&lt;/code&gt; password (credential type &lt;strong&gt;API&lt;/strong&gt;) from VCF Operations kept failing after about a minute with error code &lt;strong&gt;VCFOPSPWID03&lt;/strong&gt; and a spectacularly unhelpful message: &lt;em&gt;&amp;ldquo;call from NSX received invalid response.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The actual cause turned out to be simple — NSX rejected the new password because it had &lt;strong&gt;already been used&lt;/strong&gt; on that manager. This being a lab, I was deliberately recycling a previous password. That goes against best practices, and NSX enforces it. But the way the error surfaces (and the way the reuse policy behaves in this build) is worth writing down.&lt;/p&gt;</description><content:encoded>&lt;p&gt;While rotating credentials in the &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;same 9.1 environment&lt;/a&gt; I covered in the &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;expired password sync post&lt;/a&gt;, an &lt;strong&gt;Update&lt;/strong&gt; of the NSX Manager &lt;code&gt;admin&lt;/code&gt; password (credential type &lt;strong&gt;API&lt;/strong&gt;) from VCF Operations kept failing after about a minute with error code &lt;strong&gt;VCFOPSPWID03&lt;/strong&gt; and a spectacularly unhelpful message: &lt;em&gt;&amp;ldquo;call from NSX received invalid response.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The actual cause turned out to be simple — NSX rejected the new password because it had &lt;strong&gt;already been used&lt;/strong&gt; on that manager. This being a lab, I was deliberately recycling a previous password. That goes against best practices, and NSX enforces it. But the way the error surfaces (and the way the reuse policy behaves in this build) is worth writing down.&lt;/p&gt;
&lt;h2 id="the-error-call-from-nsx-received-invalid-response"&gt;The Error: &amp;ldquo;Call from NSX Received Invalid Response&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;The password update banner in VCF Operations:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/nsx-vcfopspwid03-password-reuse/024-capture-024.webp" alt="VCF Operations banner showing the NSX appliance password update failed because the call from NSX received an invalid response." width="1115" height="61" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The failed request under &lt;strong&gt;Management Tasks&lt;/strong&gt;, with the task failing in Stage 1 after roughly a minute:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/nsx-vcfopspwid03-password-reuse/025-capture-025.webp" alt="Update Password task in VCF Operations showing Request Failed with error code VCFOPSPWID03 after one minute in Stage 1." width="1402" height="528" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The full error text:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error Code: VCFOPSPWID03
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Password task UPDATE for &amp;lt;nsx-manager-fqdn&amp;gt; has failed. Error message:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NSX password update failed at &amp;lt;nsx-manager-fqdn&amp;gt;: Failed to update password
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;for NSX user admin for &amp;lt;nsx-manager-fqdn&amp;gt;, as call from NSX received
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;invalid response.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The same failure is visible on the VCF Operations node in the &lt;code&gt;component-manager-plugin-*.log&lt;/code&gt; (in a support bundle it is under &lt;code&gt;logs/&lt;/code&gt;; on the appliance under &lt;code&gt;/storage/log/vcops/log/&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[...UpdatePasswordTask.execute] - Executing Update password task...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[...PasswordTaskUtil.monitorPasswordTask] - Password Task : &amp;lt;task-id&amp;gt; Status: IN_PROGRESS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[...PasswordTaskUtil.monitorPasswordTask] - Password Task : &amp;lt;task-id&amp;gt; Status: FAILED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[...Task.injectFailedEventWithLocale] - Injecting task failure event. Error Code : &amp;#39;VCFOPSPWID03&amp;#39;, Retry : &amp;#39;false&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;java.lang.RuntimeException: Password task UPDATE for &amp;lt;nsx-manager-fqdn&amp;gt; has failed. [...]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;as call from NSX received invalid response.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;ldquo;Invalid response&amp;rdquo; suggests connectivity or certificate problems, and that is exactly the wrong place to go digging. NSX was up, reachable, and answering — it just did not like the request.&lt;/p&gt;
&lt;h2 id="finding-the-real-error"&gt;Finding the Real Error&lt;/h2&gt;
&lt;p&gt;Drill into the failed password task and expand the details. The &lt;code&gt;Cause&lt;/code&gt; field carries the message that the top-level error flattened away:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/nsx-vcfopspwid03-password-reuse/027-capture-027.webp" alt="Failed password update task details showing the cause: specified password invalid, password has been already used, choose another." width="1800" height="359" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Description: Password update for resource : &amp;lt;nsx-manager-fqdn&amp;gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; user : admin and credential type : API
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Progress Messages: Failed to get NSX user details.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cause: Specified password invalid: Password has been already
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; used. Choose another.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There it is. Not a connectivity issue, not a certificate issue — &lt;strong&gt;password reuse&lt;/strong&gt;. NSX checked the new password against its password history and refused it.&lt;/p&gt;
&lt;h2 id="the-nsx-password-reuse-policy"&gt;The NSX Password Reuse Policy&lt;/h2&gt;
&lt;p&gt;Password history on NSX is not the classic PAM &lt;code&gt;/etc/security/opasswd&lt;/code&gt; mechanism (mine was empty — that was my first dead end). It is governed by the &lt;code&gt;password_remembrance&lt;/code&gt; setting in the node authentication policy, alongside the complexity settings.&lt;/p&gt;
&lt;p&gt;Prompt for the admin password once instead of putting it inline — passwords containing &lt;code&gt;!&lt;/code&gt; get mangled by history expansion in interactive shells (&lt;code&gt;set +H&lt;/code&gt; disables that in bash; in zsh use &lt;code&gt;unsetopt BANG_HIST&lt;/code&gt;), and inline credentials linger in shell history:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; +H
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;NSX admin password: &amp;#34;&lt;/span&gt; NSXPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api_failed_auth_lockout_period&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api_max_auth_failures&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;minimum_password_length&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;password_remembrance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or on the NSX CLI: &lt;code&gt;get password-complexity&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Per the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/authentication-and-authorization/authentication-policy-settings.html"&gt;VCF 9.1 NSX documentation&lt;/a&gt;, &lt;code&gt;password_remembrance&lt;/code&gt; is the number of previous passwords a user cannot reuse. Three details matter here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NSX supports &lt;strong&gt;one password policy&lt;/strong&gt; — you cannot exempt a single user like &lt;code&gt;admin&lt;/code&gt; from history checks — and the manager password-policy settings synchronize across the management cluster nodes.&lt;/li&gt;
&lt;li&gt;In VCF 9.1 the &lt;strong&gt;default value is 5&lt;/strong&gt; and the documented &lt;strong&gt;valid range is 1 to 12&lt;/strong&gt; for new configurations. Out of the box, the last five passwords are blocked — which is why my recycled lab password was refused without anyone ever hardening this environment. (The same doc notes that policy settings customized before an upgrade are retained afterward, so upgraded environments may be carrying older values.)&lt;/li&gt;
&lt;li&gt;Older NSX 4.x documentation describes &lt;code&gt;0&lt;/code&gt; as &amp;ldquo;check disabled&amp;rdquo;. This 9.1 build rejects &lt;code&gt;0&lt;/code&gt; outright, as I found out next.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These examples use &lt;code&gt;curl -k&lt;/code&gt;, which skips TLS certificate verification — fine in a lab where the manager still has a self-signed certificate. In production, drop &lt;code&gt;-k&lt;/code&gt; and trust the NSX CA instead. When you are done, &lt;code&gt;unset NSXPASS&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="why-you-cannot-just-disable-password-history"&gt;Why You Cannot Just Disable Password History&lt;/h2&gt;
&lt;p&gt;My first instinct was to turn the history check off entirely, since &lt;code&gt;0&lt;/code&gt; was the documented disabled/default value in NSX 4.x. Both the CLI (&lt;code&gt;set password-complexity&lt;/code&gt;) and the API refuse it in this build:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;password_remembrance&amp;#34;: 0}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;error_code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;error_message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Request body not valid - PasswordComplexityProperties.password_remembrance: value &amp;#39;0&amp;#39; must be greater than or equal to &amp;#39;1&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;module_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;common-services&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That matches the 9.1 documentation, which lists a valid range of &lt;strong&gt;1 to 12&lt;/strong&gt; for new &lt;code&gt;password_remembrance&lt;/code&gt; configurations. In this build there is no accepted way to set &lt;code&gt;0&lt;/code&gt; — the lowest you can configure is &lt;code&gt;1&lt;/code&gt;, which blocks only the single most recent password.&lt;/p&gt;
&lt;h2 id="recommended-fix-use-a-new-compliant-password"&gt;Recommended Fix: Use a New Compliant Password&lt;/h2&gt;
&lt;p&gt;In production — and honestly in most labs too — the right response to &lt;em&gt;&amp;ldquo;Password has been already used&amp;rdquo;&lt;/em&gt; is the boring one: leave the history policy alone, pick a &lt;strong&gt;new&lt;/strong&gt; password that satisfies the policy (minimum length, character classes, no dictionary-based words, not among the remembered previous passwords), and re-run the &lt;strong&gt;Update&lt;/strong&gt; action in VCF Operations. The workflow completes, and the appliance password, the stored credential, and the expiration metadata all stay in sync.&lt;/p&gt;
&lt;p&gt;The next section covers the lab case, where re-using one specific password was the whole point.&lt;/p&gt;
&lt;h2 id="lab-only-workaround-temporarily-lower-password-history-to-1"&gt;Lab-Only Workaround: Temporarily Lower Password History to 1&lt;/h2&gt;
&lt;p&gt;Treat this as a reversible change, not a new setting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Confirm the cause first.&lt;/strong&gt; The failed task&amp;rsquo;s &lt;code&gt;Cause&lt;/code&gt; must actually say &lt;em&gt;&amp;ldquo;Password has been already used&amp;rdquo;&lt;/em&gt; — this workaround fixes nothing else.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Record the current value&lt;/strong&gt;, so you can restore it exactly:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="p"&gt;|&lt;/span&gt; jq .password_remembrance
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;Lower it to &lt;code&gt;1&lt;/code&gt;&lt;/strong&gt; — the minimum of the valid range — so NSX only blocks reuse of the immediately previous password; anything older becomes acceptable again:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;password_remembrance&amp;#34;: 1}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="4"&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Read the value back before retrying.&lt;/strong&gt; API policy changes take about 20 seconds to apply and synchronize across the manager cluster nodes — rerun the &lt;code&gt;GET&lt;/code&gt; from step 2 and confirm it returns &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Re-run the Update action in VCF Operations.&lt;/strong&gt; In my case the task completed with the recycled password, and because the change went through the supported workflow, the appliance password, the stored credential, and the expiration metadata all ended up in sync — no &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;manual vault surgery&lt;/a&gt; required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Restore the exact value you recorded in step 2&lt;/strong&gt; — not &amp;ldquo;whatever the default is&amp;rdquo;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;password_remembrance&amp;#34;: &amp;lt;original-value&amp;gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two follow-ups worth knowing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Even with &lt;code&gt;password_remembrance: 1&lt;/code&gt;, you can never &amp;ldquo;change&amp;rdquo; to the password that is currently set — the workflow requires current and new to differ — and the most recent entry in the history may still be blocked. If you absolutely must land back on a very recently used password (labs again), rotate through one or two throwaway passwords first.&lt;/li&gt;
&lt;li&gt;Keep an eye on &lt;code&gt;api_max_auth_failures&lt;/code&gt; — enough failed update attempts with a wrong current password will lock the account on top of everything else.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="if-reuse-is-not-your-problem"&gt;If Reuse Is Not Your Problem&lt;/h2&gt;
&lt;p&gt;The same flattened &lt;em&gt;&amp;ldquo;call from NSX received invalid response&amp;rdquo;&lt;/em&gt; error can hide any password policy rejection, not just reuse. Start with the failed task&amp;rsquo;s &lt;code&gt;Cause&lt;/code&gt; field — in this environment it carried NSX&amp;rsquo;s real error text, and reading it costs nothing.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;Cause&lt;/code&gt; is still too vague, you can provoke NSX into stating its verbatim reason by attempting the change directly against the node API — with one important caveat:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This is not a validation call.&lt;/strong&gt; &lt;code&gt;PUT /api/v1/node/users/&amp;lt;userid&amp;gt;&lt;/code&gt; is the real password-change API — there is no dry-run. If the request &lt;em&gt;succeeds&lt;/em&gt;, the NSX &lt;code&gt;admin&lt;/code&gt; password has just changed outside VCF Operations: the Fleet vault and SDDC Manager still hold the old credential, and every workflow that authenticates to NSX inherits that drift until you rotate once more through the VCF Operations &lt;strong&gt;Update&lt;/strong&gt; action (entering the just-set password as the current one). Only run this with a password you actually intend to keep.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First find the user ID (the built-in &lt;code&gt;admin&lt;/code&gt; is typically &lt;code&gt;10000&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/users
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then attempt the change, letting &lt;code&gt;jq&lt;/code&gt; build the payload so special characters survive the shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;New password: &amp;#34;&lt;/span&gt; NEWPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq -nc --arg old &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --arg new &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NEWPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{password: $new, old_password: $old}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/users/&amp;lt;userid&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; NEWPASS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Whatever check is failing comes back in plain text: reuse, minimum length, character classes, or the Linux PAM dictionary baseline. That last one deserves a mention: the docs state the default password complexity rules &amp;ldquo;as enforced by the Linux PAM module&amp;rdquo; always apply on top of the configurable settings, and there is no auth-policy knob to turn them off. Dictionary-based passwords — including the &lt;code&gt;&amp;lt;VendorName&amp;gt;1!&lt;/code&gt;-style classics every lab inherits — get rejected on API-driven changes even when they satisfy length and character-class rules. If the error says &amp;ldquo;dictionary word&amp;rdquo; or &amp;ldquo;too simplistic,&amp;rdquo; no &lt;code&gt;password_remembrance&lt;/code&gt; setting will save you; pick a password without recognizable words.&lt;/p&gt;
&lt;p&gt;If the direct call succeeds but the VCF Operations workflow still fails, then you genuinely have a workflow-side problem, and the &lt;code&gt;component-manager-plugin-*.log&lt;/code&gt; on the VCF Operations node is the place to look — but remember the vault is now behind, so plan one more rotation through &lt;strong&gt;Update&lt;/strong&gt; once the workflow is fixed.&lt;/p&gt;
&lt;h2 id="verification"&gt;Verification&lt;/h2&gt;
&lt;p&gt;After the update task completes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Update Password&lt;/strong&gt; request in Management Tasks completes successfully.&lt;/li&gt;
&lt;li&gt;The new password authenticates against the NSX API — a quick policy read doubles as a credential check (prompt again so the variable holds the &lt;em&gt;new&lt;/em&gt; password, and &lt;code&gt;unset NSXPASS&lt;/code&gt; when done):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;New NSX admin password: &amp;#34;&lt;/span&gt; NSXPASS&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -u admin:&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NSXPASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://&amp;lt;nsx-manager-fqdn&amp;gt;/api/v1/node/aaa/auth-policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;UI login to NSX Manager with the new password works.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;password_remembrance&lt;/code&gt; is restored to the exact value you recorded before the workaround, not whatever the troubleshooting session left behind.&lt;/li&gt;
&lt;li&gt;The password entry in Fleet shows a healthy status and refreshed expiration metadata — the &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;account query from the expired password sync post&lt;/a&gt; works for this.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="final-notes"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;The error message is the villain of this story. &lt;em&gt;&amp;ldquo;Call from NSX received invalid response&amp;rdquo;&lt;/em&gt; sent me looking at connectivity and certificates, when the task details had the real answer one click deeper: &lt;em&gt;&amp;ldquo;Password has been already used. Choose another.&amp;rdquo;&lt;/em&gt; When a VCF Operations password task fails with VCFOPSPWID03, expand the task and read the &lt;code&gt;Cause&lt;/code&gt; before touching anything else.&lt;/p&gt;
&lt;p&gt;The second lesson is that the NSX build in VCF 9.1 rejects disabling password history — new &lt;code&gt;password_remembrance&lt;/code&gt; values are limited to &lt;code&gt;1&lt;/code&gt; through &lt;code&gt;12&lt;/code&gt;, with a default of &lt;code&gt;5&lt;/code&gt;. In a lab, temporarily dropping it to &lt;code&gt;1&lt;/code&gt; is a reasonable escape hatch for recycling a password, as long as you restore the original value afterward. In production, reusing passwords is the actual problem — use a new compliant password and keep the history policy strict.&lt;/p&gt;</content:encoded></item><item><title>Fixing Expired Password Sync in VCF 9.1 Fleet Management</title><link>https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/</link><pubDate>Tue, 07 Jul 2026 17:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/</guid><description>&lt;p&gt;In VCF 9.1, Fleet Password Management accounts can get stuck in &lt;strong&gt;EXPIRED&lt;/strong&gt; status, and the password workflows changed compared to 9.0. In 9.0, you may have seen UI actions such as &lt;strong&gt;Disconnected&lt;/strong&gt; or &lt;strong&gt;Remediate&lt;/strong&gt;. In the &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;9.1 environment I was working on&lt;/a&gt;, those actions were not available. The UI exposed &lt;strong&gt;Update&lt;/strong&gt;, and the Fleet Password Management API provides the same kind of workflow: query password accounts, then update or rotate the password.&lt;/p&gt;</description><content:encoded>&lt;p&gt;In VCF 9.1, Fleet Password Management accounts can get stuck in &lt;strong&gt;EXPIRED&lt;/strong&gt; status, and the password workflows changed compared to 9.0. In 9.0, you may have seen UI actions such as &lt;strong&gt;Disconnected&lt;/strong&gt; or &lt;strong&gt;Remediate&lt;/strong&gt;. In the &lt;a href="https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/"&gt;9.1 environment I was working on&lt;/a&gt;, those actions were not available. The UI exposed &lt;strong&gt;Update&lt;/strong&gt;, and the Fleet Password Management API provides the same kind of workflow: query password accounts, then update or rotate the password.&lt;/p&gt;
&lt;h2 id="symptoms"&gt;Symptoms&lt;/h2&gt;
&lt;p&gt;The issue in this case was simple to describe but easy to chase in the wrong place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some password accounts showed as &lt;strong&gt;EXPIRED&lt;/strong&gt; in Fleet password management.&lt;/li&gt;
&lt;li&gt;The VCF Operations &lt;code&gt;root&lt;/code&gt; password had already been changed outside Fleet.&lt;/li&gt;
&lt;li&gt;The 9.1 UI did not expose the older remediation-style options, so the useful paths were the &lt;strong&gt;Update&lt;/strong&gt; action for single accounts and the API for repeatable remediation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post walks through the recovery path I used for VCF 9.1.&lt;/p&gt;
&lt;h2 id="what-changed-in-vcf-91-password-management"&gt;What Changed in VCF 9.1 Password Management&lt;/h2&gt;
&lt;p&gt;The important realization is that VCF 9.1 Fleet Password Management does not expose a documented &amp;ldquo;store this already changed password in the vault&amp;rdquo; workflow.&lt;/p&gt;
&lt;p&gt;The UI exposes &lt;strong&gt;Update&lt;/strong&gt; for a password account. For automation or for working through a larger list of expired entries, the official API exposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /suite-api/api/fleet-management/password-management/accounts/query&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT /suite-api/api/fleet-management/password-management/accounts/{passwordAccountKey}/password&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The update workflow requires both the current password and the new password. In the API these fields are &lt;code&gt;currentPassword&lt;/code&gt; and &lt;code&gt;newPassword&lt;/code&gt;; in the UI they are entered through the &lt;strong&gt;Update&lt;/strong&gt; action. Either way, this is a rotation workflow. If a password was already changed directly on the appliance, you need to provide that current real password and rotate once more through Fleet so the appliance password, the vault, and the expiration metadata are back in sync.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.broadcom.com/xapis/vcf-operations-api/latest/suite-api/api/fleet-management/password-management/accounts/query/post/"&gt;Get VCF Password Accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.broadcom.com/xapis/vcf-operations-api/latest/suite-api/api/fleet-management/password-management/accounts/passwordAccountKey/password/put/"&gt;Update Password&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.broadcom.com/xapis/vcf-operations-api/latest/"&gt;VCF Operations API Authentication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="get-a-vcf-operations-api-token"&gt;Get a VCF Operations API Token&lt;/h2&gt;
&lt;p&gt;The VCF Operations API supports &lt;code&gt;OpsToken&lt;/code&gt; authorization. Set the VCF Operations API host first:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;OPS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://&amp;lt;vcf-operations-fqdn&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These examples use &lt;code&gt;curl -k&lt;/code&gt;, which skips TLS certificate verification — common in labs where the appliance still has its self-signed certificate. With &lt;code&gt;-k&lt;/code&gt; you are sending credentials to whatever answers on that FQDN. In production, drop &lt;code&gt;-k&lt;/code&gt; and trust the VCF Operations CA instead, for example &lt;code&gt;curl --cacert /path/to/vcf-ops-ca.pem ...&lt;/code&gt;, or add the CA to the system trust store.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use an account that is authorized to call the Fleet password management APIs. Avoid echoing the password back to the terminal. The &lt;code&gt;read -rp&lt;/code&gt; / &lt;code&gt;read -rsp&lt;/code&gt; prompts in this post use bash syntax; if your interactive shell is zsh, either start &lt;code&gt;bash&lt;/code&gt; first or use the zsh form, for example &lt;code&gt;read -rs 'VCFOPS_PASSWORD?VCF Operations password: '&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Acquire a token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rp &lt;span class="s2"&gt;&amp;#34;VCF Operations username: &amp;#34;&lt;/span&gt; VCFOPS_USERNAME
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;VCF Operations password: &amp;#34;&lt;/span&gt; VCFOPS_PASSWORD&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;jq -nc &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg u &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCFOPS_USERNAME&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --arg p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCFOPS_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{username:$u,password:$p}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; curl -sk -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/auth/token/acquire&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq -r .token&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; VCFOPS_PASSWORD
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;null&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Token acquisition failed&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Test the token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: OpsToken &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/versions/current&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="query-expired-password-accounts"&gt;Query Expired Password Accounts&lt;/h2&gt;
&lt;p&gt;Now query the expired password entries:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: OpsToken &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;status&amp;#34;:&amp;#34;EXPIRED&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/fleet-management/password-management/accounts/query?pageSize=200&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee /tmp/expired-password-accounts.json &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For a cleaner table:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq -r &lt;span class="s1"&gt;&amp;#39;.vcfPasswordAccounts[] |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; [.appliance, .applianceFqdn, .userName, .credentialType, .expiryDate, .passwordAccountKey] |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; @tsv&amp;#39;&lt;/span&gt; /tmp/expired-password-accounts.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I usually sort the output into a tracking table like this:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Appliance&lt;/th&gt;
					&lt;th&gt;FQDN&lt;/th&gt;
					&lt;th&gt;User&lt;/th&gt;
					&lt;th&gt;Credential Type&lt;/th&gt;
					&lt;th&gt;Action&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF_OPERATIONS&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;vcf-operations-fqdn&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;root&lt;/td&gt;
					&lt;td&gt;SSH&lt;/td&gt;
					&lt;td&gt;Update in UI or rotate through API&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SDDC_MANAGER&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;sddc-manager-fqdn&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;root&lt;/td&gt;
					&lt;td&gt;SSH&lt;/td&gt;
					&lt;td&gt;Confirm current password, then rotate&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF_SERVICES_RUNTIME&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;vcf-services-runtime-fqdn&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;vmware-system-user&lt;/td&gt;
					&lt;td&gt;SSH&lt;/td&gt;
					&lt;td&gt;Confirm ownership before rotating&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Do not blindly rotate everything at once. Some accounts may be service users, some may belong to appliances that are temporarily unreachable, and some may be stale entries for components that no longer exist. Also note that domain-level credentials such as ESXi, vCenter, and NSX are owned by SDDC Manager rather than the Fleet vault — those rotate through the &lt;a href="https://cosmin.us/mastering-the-v1-credentials-api-in-sddc-manager-for-enhanced-security-management/"&gt;SDDC Manager /v1/credentials API&lt;/a&gt; instead.&lt;/p&gt;
&lt;h2 id="update-an-expired-password-account"&gt;Update an Expired Password Account&lt;/h2&gt;
&lt;p&gt;For a single account, you can do this from the VCF Operations UI by selecting the expired password account and using &lt;strong&gt;Update&lt;/strong&gt;. Enter the current working password and the new final password.&lt;/p&gt;
&lt;p&gt;For repeatable remediation, use the API equivalent below.&lt;/p&gt;
&lt;p&gt;One important CLI note: do not paste passwords with special characters directly into an inline curl or shell variable assignment. Passwords that contain an exclamation mark can be changed or rejected by the shell before they ever reach the API because &lt;code&gt;!&lt;/code&gt; may trigger history expansion in interactive shells.&lt;/p&gt;
&lt;p&gt;If you are using Bash, disable history expansion for the session:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; +H
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are using Zsh, disable bang history expansion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;unsetopt BANG_HIST
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The safer pattern is to prompt for the password and let &lt;code&gt;jq&lt;/code&gt; build the JSON payload.&lt;/p&gt;
&lt;p&gt;Pick one expired entry and export the key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;passwordAccountKey&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run the update call. The &lt;code&gt;currentPassword&lt;/code&gt; must be the password that works on the target right now. The &lt;code&gt;newPassword&lt;/code&gt; is the new final password you want Fleet to set and store.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;Current password: &amp;#34;&lt;/span&gt; CUR&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -rsp &lt;span class="s2"&gt;&amp;#34;New password: &amp;#34;&lt;/span&gt; NEW&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq -nc --arg c &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CUR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --arg n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NEW&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{currentPassword:$c,newPassword:$n}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -X PUT &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: OpsToken &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d @- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/fleet-management/password-management/accounts/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/password&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee /tmp/pwupdate-task.json &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; CUR NEW
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The response should create a password update workflow request. Track the task in VCF Operations, or use the returned request ID if your environment exposes the workflow request endpoint.&lt;/p&gt;
&lt;p&gt;After the task completes, query the account again. Filter by the FQDN of the appliance you just updated — it is in your tracking table or &lt;code&gt;/tmp/expired-password-accounts.json&lt;/code&gt; — so the result does not depend on how many password accounts the fleet has:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: OpsToken &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;applianceFqdn&amp;#34;:&amp;#34;&amp;lt;appliance-fqdn&amp;gt;&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/fleet-management/password-management/accounts/query?pageSize=200&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq --arg KEY &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.vcfPasswordAccounts[] | select(.passwordAccountKey==$KEY)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You want to see the account move out of &lt;code&gt;EXPIRED&lt;/code&gt; and show a new &lt;code&gt;expiryDate&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="if-the-password-was-already-changed-outside-fleet"&gt;If the Password Was Already Changed Outside Fleet&lt;/h2&gt;
&lt;p&gt;This was the awkward case in my environment.&lt;/p&gt;
&lt;p&gt;If the password has already been changed manually on the appliance, the Fleet vault may still have the old value. Since 9.1 does not provide a documented vault-only update, use the actual current appliance password as the current password and rotate once more to a new final password through the UI &lt;strong&gt;Update&lt;/strong&gt; action or the API.&lt;/p&gt;
&lt;p&gt;That gives Fleet a successful workflow and lets it update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The appliance password&lt;/li&gt;
&lt;li&gt;The stored credential&lt;/li&gt;
&lt;li&gt;The expiration metadata&lt;/li&gt;
&lt;li&gt;The password account status&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to keep the exact same password that was already set manually, that is where the product gap shows up. The documented workflow wants a change from current to new.&lt;/p&gt;
&lt;h2 id="if-the-current-password-is-unknown"&gt;If the Current Password Is Unknown&lt;/h2&gt;
&lt;p&gt;If nobody knows the current password, Fleet cannot update it because the workflow requires the current password.&lt;/p&gt;
&lt;p&gt;Use the native recovery method for that appliance first. For example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the VM console.&lt;/li&gt;
&lt;li&gt;Reset the account to a temporary known password.&lt;/li&gt;
&lt;li&gt;Confirm SSH or console login works.&lt;/li&gt;
&lt;li&gt;Immediately rotate from temporary to final through the UI &lt;strong&gt;Update&lt;/strong&gt; action or the Fleet API.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Do not leave the temporary password as the final state. The point is to re-enter the supported Fleet workflow as quickly as possible.&lt;/p&gt;
&lt;h2 id="if-an-entry-looks-stale"&gt;If an Entry Looks Stale&lt;/h2&gt;
&lt;p&gt;Before rotating, verify that the appliance still exists and belongs to the environment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: OpsToken &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;applianceFqdn&amp;#34;:&amp;#34;&amp;lt;appliance-fqdn&amp;gt;&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/fleet-management/password-management/accounts/query?pageSize=50&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also check DNS and reachability from the shell where you are running the remediation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;getent hosts &amp;lt;appliance-fqdn&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -I https://&amp;lt;appliance-fqdn&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the component has been removed but the password entry remains, do not delete database rows manually. Open a support case or use the &lt;a href="https://cosmin.us/scripted-components-cleanup-from-vcf-operations-9-1/"&gt;supported inventory cleanup path for that component&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="verification-checklist"&gt;Verification Checklist&lt;/h2&gt;
&lt;p&gt;After the expired entries are addressed, verify the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The expired account query returns fewer entries, ideally zero for the scope you remediated.&lt;/li&gt;
&lt;li&gt;Updated accounts show a refreshed &lt;code&gt;expiryDate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The password update workflow completed successfully.&lt;/li&gt;
&lt;li&gt;Direct login to the target appliance works with the new password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Query all remaining expired entries:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: OpsToken &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;status&amp;#34;:&amp;#34;EXPIRED&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OPS&lt;/span&gt;&lt;span class="s2"&gt;/suite-api/api/fleet-management/password-management/accounts/query?pageSize=200&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;.vcfPasswordAccounts&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, clean up the working files, since they contain fleet inventory details such as appliance FQDNs, usernames, and password account keys:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -f /tmp/expired-password-accounts.json /tmp/pwupdate-task.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="final-notes"&gt;Final Notes&lt;/h2&gt;
&lt;p&gt;The main lesson is that VCF 9.1 password management needs to be treated as an update or rotation workflow. The password account state is exposed through VCF Operations Fleet Password Management, and expired entries need to be handled through the UI &lt;strong&gt;Update&lt;/strong&gt; action or the documented account query and password update API calls. This is the same rotate-only philosophy the platform has had since the &lt;a href="https://cosmin.us/changing-passwords-for-the-vrealize-suite-via-vrslcm-vrealize-suite-lifecycle-manager/"&gt;vRSLCM Locker password management&lt;/a&gt; days — the tooling changed, the workflow shape did not.&lt;/p&gt;
&lt;p&gt;For this kind of issue, I would avoid two shortcuts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not use VCF 9.0 UI guidance for VCF 9.1.&lt;/li&gt;
&lt;li&gt;Do not update Fleet LCM database rows by hand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The reliable path is to update expired accounts through the UI or API and verify the resulting password account state.&lt;/p&gt;</content:encoded></item><item><title>Upgrading VCF 9.1 to 9.1.0.0300 Step by Step</title><link>https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/</link><pubDate>Sun, 05 Jul 2026 17:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/upgrading-vcf-9-1-to-9-1-0-0300-step-by-step/</guid><description>&lt;p&gt;The 9.1.0.0300 patch train for VMware Cloud Foundation 9.1 is applied through a few different lifecycle views. In my lab, the update covered the Fleet Lifecycle component, VCF Operations, SDDC Lifecycle, and SDDC Manager.&lt;/p&gt;
&lt;p&gt;Before starting, review the official Broadcom release notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-installer/sddc-manager-9-1-0-0300-release-notes.html"&gt;SDDC Manager 9.1.0.0300 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-operations/9-1-0-0300.html"&gt;VCF Operations 9.1.0.0300 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Broadcom describes these patch releases as time-sensitive fixes that can be applied between major, minor, and maintenance releases. That also means every component may not move at the same time, so check what is actually offered in your environment instead of assuming the whole VCF bill of materials changes together.&lt;/p&gt;</description><content:encoded>&lt;p&gt;The 9.1.0.0300 patch train for VMware Cloud Foundation 9.1 is applied through a few different lifecycle views. In my lab, the update covered the Fleet Lifecycle component, VCF Operations, SDDC Lifecycle, and SDDC Manager.&lt;/p&gt;
&lt;p&gt;Before starting, review the official Broadcom release notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-installer/sddc-manager-9-1-0-0300-release-notes.html"&gt;SDDC Manager 9.1.0.0300 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/release-notes/patch-releases-9-1-0-x/vcf-operations/9-1-0-0300.html"&gt;VCF Operations 9.1.0.0300 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Broadcom describes these patch releases as time-sensitive fixes that can be applied between major, minor, and maintenance releases. That also means every component may not move at the same time, so check what is actually offered in your environment instead of assuming the whole VCF bill of materials changes together.&lt;/p&gt;
&lt;h2 id="versions-in-this-upgrade"&gt;Versions in This Upgrade&lt;/h2&gt;
&lt;p&gt;Here is what was upgraded in this run.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Component&lt;/th&gt;
					&lt;th&gt;From&lt;/th&gt;
					&lt;th&gt;To&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Fleet Lifecycle&lt;/td&gt;
					&lt;td&gt;9.1.0.0.25371109&lt;/td&gt;
					&lt;td&gt;9.1.0.0300.25536195&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF Operations&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25500350&lt;/td&gt;
					&lt;td&gt;9.1.0.0300.25531073&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SDDC Lifecycle&lt;/td&gt;
					&lt;td&gt;9.1.0.0200.25495903&lt;/td&gt;
					&lt;td&gt;9.1.0.0300.25536194&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SDDC Manager&lt;/td&gt;
					&lt;td&gt;9.1.0.0200 build 25496021&lt;/td&gt;
					&lt;td&gt;9.1.0.0300 build 25536191&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As always, take backups or snapshots that match your normal rollback procedure, confirm that the Broadcom depot is reachable, and make sure no other lifecycle operations are running before you start.&lt;/p&gt;
&lt;h2 id="start-in-vcf-management"&gt;Start in VCF Management&lt;/h2&gt;
&lt;p&gt;Log in to VCF Operations and go to &lt;strong&gt;Build -&amp;gt; Lifecycle -&amp;gt; VCF Management -&amp;gt; Upgrade&lt;/strong&gt;. If the lifecycle metadata has not synced recently, use &lt;strong&gt;Sync&lt;/strong&gt; before checking for available target versions.&lt;/p&gt;
&lt;p&gt;The Fleet Lifecycle card is the first item I updated.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/001-capture-001.webp" alt="VCF Operations Build Lifecycle page showing the Fleet Lifecycle upgrade card and target version selector." width="1592" height="310" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click the target version selector and choose the 9.1.0.0300 target.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/002-navigate-to-vcf-operations-build-lifecycle-vcf-management-upgrade-sync.webp" alt="Fleet Lifecycle Component Target Version dialog showing 9.1.0.0300.25536195 as an available target version." width="773" height="214" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;After selecting the target, the Fleet Lifecycle card should show the upgrade path to &lt;strong&gt;9.1.0.0300.25536195&lt;/strong&gt;. Click &lt;strong&gt;Upgrade&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/004-capture-004.webp" alt="Fleet Lifecycle card showing the upgrade path from 9.1.0.0.25371109 to 9.1.0.0300.25536195." width="844" height="121" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-fleet-lifecycle"&gt;Upgrade Fleet Lifecycle&lt;/h2&gt;
&lt;p&gt;Once the task starts, the Fleet Lifecycle card changes to &lt;strong&gt;Upgrade in progress&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/005-capture-005.webp" alt="Fleet Lifecycle card showing upgrade in progress with a link to upgrade details." width="850" height="135" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Open &lt;strong&gt;Upgrade details&lt;/strong&gt; to monitor the subtask list. In my run, the task staged binaries, ran component stage prechecks, and initialized the VCF component stage.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/006-capture-006.webp" alt="Fleet Lifecycle upgrade details showing staging binaries and component stage precheck messages." width="1036" height="440" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;When this finishes, the Fleet Lifecycle card should show the new current version.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/007-capture-007.webp" alt="Fleet Lifecycle card showing current version 9.1.0.0300.25536195." width="751" height="357" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="set-the-vcf-management-target-version"&gt;Set the VCF Management Target Version&lt;/h2&gt;
&lt;p&gt;Next, set the management component target version. In the &lt;strong&gt;Target Version 9.1.0.*&lt;/strong&gt; section, click &lt;strong&gt;Change Target Version&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In my lab I used the wildcard &lt;strong&gt;9.1.0.*&lt;/strong&gt; global version. Select the patch release for VCF Operations and any other management components that might need to be upgraded.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/008-capture-008.webp" alt="Set Target Version dialog showing VCF global version 9.1.0.* and VCF Operations target 9.1.0.0300.25531073." width="761" height="515" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Set Version&lt;/strong&gt; and then run the prechecks. The page should show the components that are ready for upgrade.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/009-capture-009.webp" alt="VCF Management upgrade page showing VCF Operations and SDDC Lifecycle ready for upgrade." width="970" height="320" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-vcf-operations"&gt;Upgrade VCF Operations&lt;/h2&gt;
&lt;p&gt;Start with VCF Operations. The row in my environment moved from &lt;strong&gt;9.1.0.0200.25500350&lt;/strong&gt; to &lt;strong&gt;9.1.0.0300.25531073&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Upgrade&lt;/strong&gt; for the VCF Operations row.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/010-capture-010.webp" alt="VCF Operations row showing upgrade in progress from 9.1.0.0200.25500350 to 9.1.0.0300.25531073." width="822" height="96" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Use &lt;strong&gt;Upgrade details&lt;/strong&gt; to monitor the precheck and update process. In this run, the task started an appliance precheck, initiated the VCF Ops precheck, and then began applying the system update.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/011-capture-011.webp" alt="VCF Operations upgrade details showing component upgrade prechecks and system update messages." width="1047" height="480" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;When the VCF Operations update completes, the &lt;strong&gt;Tasks&lt;/strong&gt; tab should show the refresh and upgrade tasks as completed.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/012-capture-012.webp" alt="VCF Management Tasks tab showing Refresh component VCF Operations and VCF Operations Upgrade completed." width="545" height="210" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="upgrade-sddc-lifecycle"&gt;Upgrade SDDC Lifecycle&lt;/h2&gt;
&lt;p&gt;After VCF Operations is done, return to the upgrade list. SDDC Lifecycle should still show as ready if you did not upgrade everything at once.&lt;/p&gt;
&lt;p&gt;In my environment, SDDC Lifecycle moved from &lt;strong&gt;9.1.0.0200.25495903&lt;/strong&gt; to &lt;strong&gt;9.1.0.0300.25536194&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/013-capture-013.webp" alt="SDDC Lifecycle row showing ready for upgrade from 9.1.0.0200.25495903 to 9.1.0.0300.25536194." width="979" height="93" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Upgrade&lt;/strong&gt; for SDDC Lifecycle and monitor the upgrade details.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/014-capture-014.webp" alt="SDDC Lifecycle row showing upgrade in progress." width="837" height="150" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;The details panel should show the SDDC Lifecycle plugin being staged in the VCF services runtime.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/015-capture-015.webp" alt="SDDC Lifecycle upgrade details showing staging in the VCF services runtime." width="880" height="283" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Wait for the task to complete before moving on to the SDDC Manager upgrade plan.&lt;/p&gt;
&lt;h2 id="download-the-sddc-manager-patch-binary"&gt;Download the SDDC Manager Patch Binary&lt;/h2&gt;
&lt;p&gt;Now go to &lt;strong&gt;Build -&amp;gt; Lifecycle -&amp;gt; VCF Instances -&amp;gt; instance-a -&amp;gt; Binary Management&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Set &lt;strong&gt;VCF Version&lt;/strong&gt; to &lt;strong&gt;9.1&lt;/strong&gt;, select &lt;strong&gt;Patch Binaries&lt;/strong&gt;, and confirm the &lt;strong&gt;SDDC Manager 9.1.0.0300&lt;/strong&gt; binary is downloaded. In this run the binary was released on &lt;strong&gt;Jun 29, 2026&lt;/strong&gt; and was &lt;strong&gt;2.41 GB&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/016-capture-016.webp" alt="Binary Management page showing SDDC Manager 9.1.0.0300 patch binary downloaded." width="1338" height="380" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If it is not downloaded yet, select the binary and click &lt;strong&gt;Download&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="edit-the-workload-domain-upgrade-plan"&gt;Edit the Workload Domain Upgrade Plan&lt;/h2&gt;
&lt;p&gt;Go to the management domain under the VCF instance, then open the &lt;strong&gt;Upgrades&lt;/strong&gt; tab. In this example the management domain is &lt;strong&gt;mgmt-a&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Edit Plan&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/017-capture-017.webp" alt="Management domain Upgrades tab showing Edit Plan for the component upgrade plan." width="876" height="318" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Plan Component Upgrade&lt;/strong&gt;, select &lt;strong&gt;SDDC Manager&lt;/strong&gt; and choose target version &lt;strong&gt;9.1.0.0300&lt;/strong&gt;. In my run, ESX, NSX, and vCenter were already on 9.1.0.0100 and were not selected for this 0300 SDDC Manager patch plan.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/018-capture-018.webp" alt="Plan Component Upgrade wizard showing SDDC Manager selected with target version 9.1.0.0300." width="1023" height="531" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt; and review the plan. The wizard notes that SDDC Manager is upgraded first so it can continue orchestrating the remaining component lifecycle work.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/019-capture-019.webp" alt="Plan Overview page showing VMware SDDC Manager 9.1.0.0300 Upgrade with a release notes link." width="1024" height="523" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Submit Plan&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="start-or-schedule-the-sddc-manager-upgrade"&gt;Start or Schedule the SDDC Manager Upgrade&lt;/h2&gt;
&lt;p&gt;The upgrade sequence should now show the SDDC Manager 9.1.0.0300 upgrade as the available step. You can run it immediately with &lt;strong&gt;Upgrade Now&lt;/strong&gt; or schedule it for a maintenance window.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/020-capture-020.webp" alt="Upgrade Sequence page showing VMware SDDC Manager 9.1.0.0300 Upgrade with Upgrade Now and Schedule buttons." width="837" height="196" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;After starting the upgrade, monitor the &lt;strong&gt;VMware Cloud Foundation Upgrade Status&lt;/strong&gt; page. You can also click &lt;strong&gt;View Upgrade Activity&lt;/strong&gt; for deeper task progress.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/021-capture-021.webp" alt="VMware Cloud Foundation Upgrade Status page showing SDDC Manager upgrade in progress." width="1315" height="651" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;In my run, the visible steps included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run VCF Prerequisite&lt;/li&gt;
&lt;li&gt;Setup Common Appliance Platform&lt;/li&gt;
&lt;li&gt;Validate Services Before Upgrade&lt;/li&gt;
&lt;li&gt;Remove Packages Pre Upgrade&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do not refresh services or reboot appliances manually while the upgrade is running. Let the lifecycle workflow complete.&lt;/p&gt;
&lt;h2 id="validate-the-upgrade"&gt;Validate the Upgrade&lt;/h2&gt;
&lt;p&gt;After the workflow completes, return to the management domain and open &lt;strong&gt;Component Versions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The SDDC Manager row should show the same current and target version, with status &lt;strong&gt;On Target&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/07/vcf-9-1-0300-upgrade/022-capture-022.webp" alt="Component Versions page showing SDDC Manager current version and target version 9.1.0.0300.25536191 with On Target status." width="1591" height="425" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;I also recommend checking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VCF Management -&amp;gt; Tasks&lt;/strong&gt; for failed or partially completed tasks&lt;/li&gt;
&lt;li&gt;VCF Operations health and adapters&lt;/li&gt;
&lt;li&gt;SDDC Manager service health&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fleet password management&lt;/strong&gt; for accounts showing &lt;strong&gt;EXPIRED&lt;/strong&gt; — I covered the remediation in &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;Fixing Expired Password Sync in VCF 9.1 Fleet Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Workload domain upgrade status&lt;/li&gt;
&lt;li&gt;Depot/binary cleanup after you are comfortable with the rollback window&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you keep older bundles around for too long, they can consume a lot of space. I covered bundle cleanup separately in &lt;a href="https://cosmin.us/cleaning-up-vcf-bundles/"&gt;Cleaning up VCF Bundles&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Exporting and Restoring vCenter 8.x Permissions Before Breaking Enhanced Linked Mode</title><link>https://cosmin.us/exporting-and-restoring-vcenter-8-x-permissions-before-breaking-enhanced-linked-mode/</link><pubDate>Tue, 30 Jun 2026 15:45:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/exporting-and-restoring-vcenter-8-x-permissions-before-breaking-enhanced-linked-mode/</guid><description>&lt;p&gt;Breaking Enhanced Linked Mode is one of those changes where the technical command is not the hardest part. The hard part is making sure administrators, service accounts, and external products can still log in and do their jobs after the vCenters no longer share the same SSO domain.&lt;/p&gt;
&lt;p&gt;In this post I will walk through a repeatable way to export and restore vCenter 8.x RBAC data before splitting Enhanced Linked Mode. The focus is on three permission layers:&lt;/p&gt;</description><content:encoded>&lt;p&gt;Breaking Enhanced Linked Mode is one of those changes where the technical command is not the hardest part. The hard part is making sure administrators, service accounts, and external products can still log in and do their jobs after the vCenters no longer share the same SSO domain.&lt;/p&gt;
&lt;p&gt;In this post I will walk through a repeatable way to export and restore vCenter 8.x RBAC data before splitting Enhanced Linked Mode. The focus is on three permission layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Custom roles&lt;/li&gt;
&lt;li&gt;Explicit object or inventory permissions&lt;/li&gt;
&lt;li&gt;Global permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The short version is this: &lt;strong&gt;object permissions usually survive the split because they live in the vCenter database, but global permissions and SSO-domain objects need special handling&lt;/strong&gt;. If you depend on global permissions, do not start the split until you have exported them and have a plan to recreate or import them afterward.&lt;/p&gt;
&lt;h2 id="what-changes-when-you-split-elm"&gt;What Changes When You Split ELM&lt;/h2&gt;
&lt;p&gt;Enhanced Linked Mode lets multiple vCenter Server instances share one vCenter Single Sign-On domain. When you break that relationship, the vCenters stop sharing the same SSO namespace.&lt;/p&gt;
&lt;p&gt;That matters because not all permissions are stored the same way.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Item&lt;/th&gt;
					&lt;th&gt;What to expect&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Custom roles&lt;/td&gt;
					&lt;td&gt;Stored in the vCenter database, so they usually survive the split. Export them anyway and re-import if anything drifted.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Object permissions&lt;/td&gt;
					&lt;td&gt;Usually remain on the vCenter inventory objects, but the principals must still resolve after the split.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Global permissions&lt;/td&gt;
					&lt;td&gt;Must be exported and recreated/imported after the split.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Local SSO users/groups&lt;/td&gt;
					&lt;td&gt;Must be recreated if they are still needed.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;AD/LDAP identity sources&lt;/td&gt;
					&lt;td&gt;Must be recreated or verified on the standalone vCenter.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Solution users/plugins&lt;/td&gt;
					&lt;td&gt;Usually need product-specific re-registration or repair.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If the environment is managed by VMware Cloud Foundation / SDDC Manager, stop and validate supportability first. Cross-domain repointing is not something I would treat as a casual vCenter-only operation in a VCF managed environment.&lt;/p&gt;
&lt;h2 id="download-the-helper-scripts"&gt;Download the Helper Scripts&lt;/h2&gt;
&lt;p&gt;I use three helper scripts for this workflow:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Script&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/vcenter-elm-rbac/Export-VCenterRbac.ps1"&gt;Export-VCenterRbac.ps1&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Exports custom roles and explicit object permissions from a vCenter.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/vcenter-elm-rbac/Convert-AuthzDoctorPermissions.ps1"&gt;Convert-AuthzDoctorPermissions.ps1&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Converts &lt;code&gt;authz-doctor&lt;/code&gt; output into a clean CSV for global permissions.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/vcenter-elm-rbac/Import-VCenterRbac.ps1"&gt;Import-VCenterRbac.ps1&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Imports roles, optional global permissions, and object permissions after the split.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The scripts assume PowerShell 7+ and PowerCLI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;VMware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;PowerCLI&lt;/span&gt; &lt;span class="n"&gt;-Scope&lt;/span&gt; &lt;span class="n"&gt;CurrentUser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PowerCLIConfiguration&lt;/span&gt; &lt;span class="n"&gt;-InvalidCertificateAction&lt;/span&gt; &lt;span class="n"&gt;Warn&lt;/span&gt; &lt;span class="n"&gt;-Confirm:&lt;/span&gt;&lt;span class="vm"&gt;$false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run the import with &lt;code&gt;-WhatIf&lt;/code&gt; first. This is especially important when restoring global permissions.&lt;/p&gt;
&lt;h2 id="before-you-export-anything"&gt;Before You Export Anything&lt;/h2&gt;
&lt;p&gt;Do the boring checks first. This is where most bad outcomes are avoided.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Confirm which vCenters are staying linked and which vCenters are being split.&lt;/li&gt;
&lt;li&gt;Confirm you have working SSO administrator access.&lt;/li&gt;
&lt;li&gt;Confirm you have root access to each VCSA so you can run &lt;code&gt;authz-doctor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Take a file-based backup of each vCenter.&lt;/li&gt;
&lt;li&gt;Take powered-off snapshots of all ELM nodes at the same point in time if that is your rollback method.&lt;/li&gt;
&lt;li&gt;If vCenter HA is enabled, remove it before the split.&lt;/li&gt;
&lt;li&gt;Identify any local SSO users/groups that need to exist after the split.&lt;/li&gt;
&lt;li&gt;Identify all AD/LDAP identity sources that need to be recreated.&lt;/li&gt;
&lt;li&gt;Identify solution accounts for NSX, SRM, Aria, backup products, monitoring tools, and plugins.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I also like to create at least one explicit vCenter-root permission for a known AD admin group on each vCenter before the change. Do not rely only on global permissions for your break-glass path.&lt;/p&gt;
&lt;h2 id="step-1-export-roles-and-object-permissions"&gt;Step 1: Export Roles and Object Permissions&lt;/h2&gt;
&lt;p&gt;Create one export folder per vCenter. Do not mix them. The object paths and managed object references are specific to the vCenter they came from.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;vcenter-elm&lt;/span&gt;&lt;span class="n"&gt;-rbac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Export-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-OutDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Export-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa02&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-OutDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa02&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Export-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa03&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-OutDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa03&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each export folder should contain:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;manifest.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;roles.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;object-permissions.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;object-permissions.csv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;global-permissions.template.csv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;roles.json&lt;/code&gt; is used for import. &lt;code&gt;object-permissions.json&lt;/code&gt; is also used for import. The CSV is there so you can review the permissions quickly in Excel or another editor.&lt;/p&gt;
&lt;p&gt;Open &lt;code&gt;manifest.json&lt;/code&gt; and make sure the count looks reasonable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Get-Content&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;json&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;ConvertFrom-Json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-2-export-global-permissions-with-authz-doctor"&gt;Step 2: Export Global Permissions with authz-doctor&lt;/h2&gt;
&lt;p&gt;Global permissions are the part I care about most in an ELM split. They are easy to forget because they live under &lt;strong&gt;Administration &amp;gt; Access Control &amp;gt; Global Permissions&lt;/strong&gt;, not on the normal inventory tree.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;authz-doctor&lt;/code&gt; tool is preinstalled on recent vCenter builds, but not on all of them. If the path below does not exist on your appliance, download the RPM attached to &lt;a href="https://knowledge.broadcom.com/external/article/369938/using-the-authzdoctor-tool-to-identify-v.html"&gt;Broadcom KB 369938&lt;/a&gt; and install it with &lt;code&gt;rpm -i --force&lt;/code&gt; first, which is the install command the KB uses.&lt;/p&gt;
&lt;p&gt;On each vCenter appliance, SSH in as &lt;code&gt;root&lt;/code&gt; and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/lib/vmware-vpx/scripts/authz-doctor/authz-doctor.py permission_check &amp;gt; /tmp/authz-doctor-permissions.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Copy the file back to the matching export folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scp root@vcsa01.example.com:/tmp/authz-doctor-permissions.txt ./exports/vcsa01/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output is a bordered ASCII table. The permission rows look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-----------------------------------+-------+---------+-----------+-----------+--------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| Principal | Group | Role Id | Role Name | Propagate | Entity |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-----------------------------------+-------+---------+-----------+-----------+--------+
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| VSPHERE.LOCAL\vpxd-extension-xxxx | False | -1 | Admin | True | Global |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| DOMAIN\vSphere-Admins | True | -1 | Admin | True | Global |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-----------------------------------+-------+---------+-----------+-----------+--------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The converter detects permission rows by their six-column layout and skips the border and header lines, so you can feed it the raw &lt;code&gt;permission_check&lt;/code&gt; output without any cleanup.&lt;/p&gt;
&lt;p&gt;Then convert the global permission rows into CSV:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Convert-AuthzDoctorPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InputFile&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;authz-doctor&lt;/span&gt;&lt;span class="n"&gt;-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-OutputFile&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;global-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-GlobalOnly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Repeat this for each vCenter.&lt;/p&gt;
&lt;p&gt;The resulting file should have this shape:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;Principal&amp;#34;,&amp;#34;IsGroup&amp;#34;,&amp;#34;RoleId&amp;#34;,&amp;#34;RoleName&amp;#34;,&amp;#34;Propagate&amp;#34;,&amp;#34;Entity&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;DOMAIN\vSphere-Admins&amp;#34;,&amp;#34;True&amp;#34;,&amp;#34;-1&amp;#34;,&amp;#34;Admin&amp;#34;,&amp;#34;True&amp;#34;,&amp;#34;Global&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-3-review-principals-before-the-split"&gt;Step 3: Review Principals Before the Split&lt;/h2&gt;
&lt;p&gt;Do not blindly import the CSV later. Review it first.&lt;/p&gt;
&lt;p&gt;Pay special attention to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;administrator@vsphere.local&lt;/code&gt; or users from the old SSO domain&lt;/li&gt;
&lt;li&gt;Local SSO groups that will not exist after the split&lt;/li&gt;
&lt;li&gt;Solution users such as &lt;code&gt;vpxd-*&lt;/code&gt;, &lt;code&gt;vsphere-ui-*&lt;/code&gt;, NSX, SRM, backup, or monitoring accounts&lt;/li&gt;
&lt;li&gt;AD groups whose domain name or identity source alias may change&lt;/li&gt;
&lt;li&gt;Custom roles that exist on one vCenter but not another&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are moving from a shared SSO domain to standalone SSO domains, a principal name that made sense before the split may not resolve afterward. The permission entry can exist, but it is useless if the identity source cannot resolve the user or group.&lt;/p&gt;
&lt;h2 id="step-4-add-temporary-object-level-admin-access"&gt;Step 4: Add Temporary Object-Level Admin Access&lt;/h2&gt;
&lt;p&gt;Before breaking ELM, add a direct permission at the root of each vCenter inventory for the admin group you will use after the split.&lt;/p&gt;
&lt;p&gt;In the vSphere Client:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the vCenter object at the top of the inventory.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Permissions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add your AD admin group.&lt;/li&gt;
&lt;li&gt;Assign the appropriate role, usually &lt;code&gt;Administrator&lt;/code&gt; for the migration window.&lt;/li&gt;
&lt;li&gt;Enable propagation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This gives you a local object permission that is independent of Global Permissions. It is your practical safety net.&lt;/p&gt;
&lt;h2 id="step-5-break-enhanced-linked-mode"&gt;Step 5: Break Enhanced Linked Mode&lt;/h2&gt;
&lt;p&gt;Follow the supported vCenter 8.x ELM split procedure for your environment. At a high level, the flow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Shut down the vCenter being split from the SSO domain.&lt;/li&gt;
&lt;li&gt;From one of the remaining linked vCenters, unregister the powered-off node.&lt;/li&gt;
&lt;li&gt;Power on the vCenter being split.&lt;/li&gt;
&lt;li&gt;Repoint it to a standalone SSO domain.&lt;/li&gt;
&lt;li&gt;Validate that the vCenter no longer shows the other linked vCenters.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Be aware that the unregister step (&lt;code&gt;cmsso-util unregister&lt;/code&gt;) restarts all vCenter services on the node where you run it, so plan for a short vSphere Client outage on that remaining vCenter, not just on the one being split.&lt;/p&gt;
&lt;p&gt;One extra step if your ELM group has only two nodes: after the repoint, &lt;a href="https://knowledge.broadcom.com/external/article/370062/splitting-enhanced-linked-mode-elm.html"&gt;Broadcom KB 370062&lt;/a&gt; says to also run &lt;code&gt;cmsso-util unregister&lt;/code&gt; on the remaining node, so the stale partner information for the node that left is released.&lt;/p&gt;
&lt;p&gt;The key point for this post is timing: &lt;strong&gt;do not import permissions until the standalone vCenter has its identity source and SSO domain in the final state&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="step-6-recreate-identity-sources-and-local-sso-objects"&gt;Step 6: Recreate Identity Sources and Local SSO Objects&lt;/h2&gt;
&lt;p&gt;After the vCenter is standalone, log in with the new SSO administrator and recreate or verify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AD/LDAP identity sources&lt;/li&gt;
&lt;li&gt;Local SSO users&lt;/li&gt;
&lt;li&gt;Local SSO groups&lt;/li&gt;
&lt;li&gt;Any SSO group nesting used by the exported permissions&lt;/li&gt;
&lt;li&gt;Certificates or trust needed for LDAPS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also check &lt;strong&gt;Global Permissions&lt;/strong&gt; for stale entries that still reference the previous SSO domain and clean them up. The split KB links a dedicated procedure for removing stale global permissions after a domain repoint, and the import is much easier to reason about when the old domain&amp;rsquo;s dead entries are gone first.&lt;/p&gt;
&lt;p&gt;Before importing permissions, test that the principal resolves. For example, search for the AD group in the vSphere Client permission picker. If the picker cannot resolve it, the import will not give you meaningful access.&lt;/p&gt;
&lt;h2 id="step-7-dry-run-the-import"&gt;Step 7: Dry-Run the Import&lt;/h2&gt;
&lt;p&gt;Start with a dry run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Import-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-GlobalPermissionsCsv&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;global-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-UsePrivateMobForGlobalPermissions&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-WhatIf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The import order is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create or update custom roles.&lt;/li&gt;
&lt;li&gt;Import global permissions if a CSV is provided.&lt;/li&gt;
&lt;li&gt;Import object permissions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The global permission import uses the vCenter MOB endpoint for &lt;code&gt;AuthorizationService.AddGlobalAccessControlList&lt;/code&gt;. That is why the switch is intentionally named &lt;code&gt;-UsePrivateMobForGlobalPermissions&lt;/code&gt;. I want it to be obvious that this is different from the normal object permission path through &lt;code&gt;AuthorizationManager&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The MOB route is specifically the vCenter 8.x answer. vSphere 9.0 added a supported REST API for global permissions (&lt;a href="https://developer.broadcom.com/xapis/vsphere-automation-api/latest/vcenter/vcenter-authorization-permissions/"&gt;&lt;code&gt;POST /api/vcenter/authorization/permissions&lt;/code&gt;&lt;/a&gt; with a &lt;code&gt;GlobalAcl&lt;/code&gt; DynamicID), so if you are doing this on 9.x, use that instead.&lt;/p&gt;
&lt;p&gt;If you only want to restore roles and global permissions first, skip object permissions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Import-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-GlobalPermissionsCsv&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;global-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-UsePrivateMobForGlobalPermissions&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-SkipObjectPermissions&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-WhatIf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="step-8-run-the-import"&gt;Step 8: Run the Import&lt;/h2&gt;
&lt;p&gt;Once the dry run looks right, run it without &lt;code&gt;-WhatIf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Import-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-GlobalPermissionsCsv&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;global-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;csv&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-UsePrivateMobForGlobalPermissions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you changed principal names during the split, edit &lt;code&gt;global-permissions.csv&lt;/code&gt; before running the import. For example, change:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;OLDSSO\vSphere Admins
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CORP\vSphere Admins
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or whatever the correct identity source now exposes.&lt;/p&gt;
&lt;h2 id="step-9-verify-access"&gt;Step 9: Verify Access&lt;/h2&gt;
&lt;p&gt;Do not stop after the script completes. Verify the permission model from both the API side and the user side.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;authz-doctor&lt;/code&gt; again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/lib/vmware-vpx/scripts/authz-doctor/authz-doctor.py permission_check &amp;gt; /tmp/authz-doctor-permissions-after.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check Global Permissions in the vSphere Client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Administration &amp;gt; Access Control &amp;gt; Global Permissions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then test real logins:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log out of the SSO administrator account.&lt;/li&gt;
&lt;li&gt;Log in as a user from the restored AD admin group.&lt;/li&gt;
&lt;li&gt;Confirm you can see the expected inventory.&lt;/li&gt;
&lt;li&gt;Confirm you can perform a low-risk administrative action.&lt;/li&gt;
&lt;li&gt;Test at least one non-admin role if you have delegated access.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For object permissions, you can also re-export and compare the count:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Export-VCenterRbac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Server&lt;/span&gt; &lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;com&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-OutDir&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;vcsa01-after&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Compare-Object&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Import-Csv&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;vcsa01&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;object-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Import-Csv&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;vcsa01-after&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;object-permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Property&lt;/span&gt; &lt;span class="n"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;RoleName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Propagate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;EntityPath&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some differences are expected if you intentionally changed principals or cleaned up stale entries. Unexpected missing admin groups are not expected.&lt;/p&gt;
&lt;h2 id="common-gotchas"&gt;Common Gotchas&lt;/h2&gt;
&lt;p&gt;The import cannot fix an identity source that does not exist. Recreate AD/LDAP first.&lt;/p&gt;
&lt;p&gt;Custom role IDs can change. The import script matches role names first because role IDs are not always stable across systems.&lt;/p&gt;
&lt;p&gt;Object permissions can point to objects that no longer exist. The script tries the original managed object reference first and then the inventory path. If neither resolves, review the CSV and decide whether the permission is still needed.&lt;/p&gt;
&lt;p&gt;Solution users are not normal human permissions. If NSX, SRM, Aria, a backup product, or a monitoring tool owns a registration, use that product&amp;rsquo;s supported reconnect or repair procedure instead of blindly restoring stale solution-user permissions.&lt;/p&gt;
&lt;p&gt;Global permissions are broad. Review them carefully before bringing them back into a standalone vCenter.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/370062/splitting-enhanced-linked-mode-elm.html"&gt;Broadcom KB 370062 - Splitting Enhanced Linked Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/369938/using-the-authzdoctor-tool-to-identify-v.html"&gt;Broadcom KB 369938 - Using authz-doctor to identify vCenter permissions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/407544/vcls-vms-deployment-fails-vcenter-report.html"&gt;Broadcom KB 407544 - vCLS VMs deployment fails (documents the &lt;code&gt;AuthorizationService.AddGlobalAccessControlList&lt;/code&gt; MOB workflow)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.broadcom.com/xapis/vsphere-web-services-api/latest/vim.AuthorizationManager.html"&gt;vSphere Web Services API - AuthorizationManager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.broadcom.com/xapis/vsphere-automation-api/latest/vcenter/vcenter-authorization-permissions/"&gt;vSphere Automation API - vCenter Authorization Permissions (vSphere 9.0+)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is not just to preserve a list of permissions. The goal is to preserve access that still resolves, still matches the right role, and still works after each vCenter stands on its own.&lt;/p&gt;</content:encoded></item><item><title>Clearing a Full vSAN Trace Ramdisk Across ESXi Hosts in Parallel</title><link>https://cosmin.us/clearing-a-full-vsan-trace-ramdisk-across-esxi-hosts-in-parallel/</link><pubDate>Wed, 24 Jun 2026 10:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/clearing-a-full-vsan-trace-ramdisk-across-esxi-hosts-in-parallel/</guid><description>&lt;p&gt;While reviewing an SOS support bundle from a VMware Cloud Foundation environment, I noticed every ESXi host in the management cluster was logging the same warning, over and over, right up to the moment the bundle was collected:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[vob.visorfs.ramdisk.full] Cannot extend visorfs file
/vsantraces/vsantracesLSOMVerbose--...zst because its ramdisk
(vsantraceFailover) is full.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These are &lt;code&gt;-INFO&lt;/code&gt; level VOB events, not errors, and they do not touch your data or VMs. But when the same symptom appears on all hosts at once, fires continuously, and never clears on its own, it is worth understanding what is actually happening and fixing it cluster-wide rather than logging into each host by hand. This post walks through the diagnosis and a small Bash script that queries, fixes, and reclaims space on every host in parallel.&lt;/p&gt;</description><content:encoded>&lt;p&gt;While reviewing an SOS support bundle from a VMware Cloud Foundation environment, I noticed every ESXi host in the management cluster was logging the same warning, over and over, right up to the moment the bundle was collected:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[vob.visorfs.ramdisk.full] Cannot extend visorfs file
/vsantraces/vsantracesLSOMVerbose--...zst because its ramdisk
(vsantraceFailover) is full.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These are &lt;code&gt;-INFO&lt;/code&gt; level VOB events, not errors, and they do not touch your data or VMs. But when the same symptom appears on all hosts at once, fires continuously, and never clears on its own, it is worth understanding what is actually happening and fixing it cluster-wide rather than logging into each host by hand. This post walks through the diagnosis and a small Bash script that queries, fixes, and reclaims space on every host in parallel.&lt;/p&gt;
&lt;h2 id="understanding-the-vsan-trace-ramdisk"&gt;Understanding the vSAN Trace Ramdisk&lt;/h2&gt;
&lt;p&gt;vSAN writes diagnostic traces (DOM, LSOM, CLOM, PLOG, and others) to &lt;code&gt;/vsantraces&lt;/code&gt;, an in-memory location backed by a ramdisk. Under normal operation these traces rotate into compressed &lt;code&gt;.zst&lt;/code&gt; archives and the ramdisk stays well under capacity. ESXi also keeps a secondary &lt;code&gt;vsantraceFailover&lt;/code&gt; ramdisk that catches trace writes when the primary cannot be extended.&lt;/p&gt;
&lt;p&gt;The warning above means the &lt;em&gt;failover&lt;/em&gt; ramdisk has hit 100% and the trace daemon can no longer write new trace data. Because tracing is purely diagnostic, the cluster keeps running normally — but you lose trace history, the logs fill with noise, and a genuinely useful troubleshooting tool is effectively offline.&lt;/p&gt;
&lt;p&gt;A few things are easy to get wrong here, so it is worth being precise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is &lt;strong&gt;not&lt;/strong&gt; caused by verbose tracing being left on. On a healthy vSAN ESA cluster, &lt;code&gt;LSOMVerbose&lt;/code&gt; is enabled by default. Confirm the configured level before assuming someone changed it.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;vsantraced&lt;/code&gt; restart re-initializes the daemon but does &lt;strong&gt;not&lt;/strong&gt; purge files already sitting on the failover ramdisk. If the ramdisk is full, restarting alone will often leave it full.&lt;/li&gt;
&lt;li&gt;The real reclaim comes from removing the old rotated &lt;code&gt;.zst&lt;/code&gt; archives, which are the bulk of the consumed space.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="diagnosing-before-you-touch-anything"&gt;Diagnosing Before You Touch Anything&lt;/h2&gt;
&lt;p&gt;The first job is to confirm the state on every host with read-only commands. Three pieces of information tell you almost everything:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# What trace level is actually configured?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;esxcli vsan trace get
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Is the failover ramdisk full?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vdf -h &lt;span class="p"&gt;|&lt;/span&gt; grep -i vsantrace
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# What is consuming the space?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls -lhS /vsantraces/ &lt;span class="p"&gt;|&lt;/span&gt; head
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If &lt;code&gt;vdf&lt;/code&gt; shows a line like &lt;code&gt;vsantraceFailover 300M 300M 0B 100%&lt;/code&gt;, that is your smoking gun. The &lt;code&gt;ls&lt;/code&gt; output will typically show several large &lt;code&gt;vsantraces--*.zst&lt;/code&gt; archives (often the configured max file size each) as the dominant consumers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on SSH:&lt;/strong&gt; ESXi SSH is disabled by default, and a recent SOS bundle will reflect that. Enable it per host first (vCenter &amp;gt; Host &amp;gt; Configure &amp;gt; Services &amp;gt; SSH &amp;gt; Start) before running anything below, and disable it again when you are done. If you would rather keep SSH off entirely, the same commands can be issued through vCenter with PowerCLI &lt;code&gt;Get-EsxCli&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="querying-every-host-at-once"&gt;Querying Every Host at Once&lt;/h2&gt;
&lt;p&gt;Logging into hosts one at a time does not scale, and the whole point is that this condition tends to hit the entire cluster together. The script below fans out over SSH: it launches one background job per host, waits for all of them, and writes each host&amp;rsquo;s output to its own file. A per-connection timeout keeps a single unreachable host from hanging the whole run.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# query-vsan-traces.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Query (and optionally fix) all ESXi hosts in parallel for the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# vSAN &amp;#34;vsantraceFailover ramdisk full&amp;#34; condition.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Usage:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./query-vsan-traces.sh # QUERY only (read-only, default)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./query-vsan-traces.sh --fix # QUERY, then restart vsantraced&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./query-vsan-traces.sh --clean # QUERY, then delete OLD .zst archives&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./query-vsan-traces.sh --clean host1 # restrict the action to specific hosts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ./query-vsan-traces.sh --clean --yes # skip the confirmation prompt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SSH_USER=root ./query-vsan-traces.sh # override the SSH user (default: root)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -u
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# --- config -----------------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;HOSTS_DEFAULT&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; esx-01.example.lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; esx-02.example.lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; esx-03.example.lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; esx-04.example.lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SSH_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_USER&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Per-connection timeouts so one unreachable host can&amp;#39;t hang the whole run.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SSH_OPTS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;-o &lt;span class="nv"&gt;ConnectTimeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; -o &lt;span class="nv"&gt;BatchMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes -o &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;accept-new&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;OUTDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;vsan-trace-report-&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date +%Y%m%d-%H%M%S&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ---------------------------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# --- arg parsing ------------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DO_FIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DO_CLEAN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ASSUME_YES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; arg in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --fix&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;DO_FIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --clean&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;DO_CLEAN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --yes&lt;span class="p"&gt;|&lt;/span&gt;-y&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ASSUME_YES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -*&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Unknown option: &lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="o"&gt;+=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTS_DEFAULT&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTDIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# The diagnostic commands run on each host. Keep them read-only.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -r -d &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; QUERY_CMDS &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;===== $(hostname) =====&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- esxcli vsan trace get ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;esxcli vsan trace get 2&amp;gt;&amp;amp;1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- ramdisk usage (vsantrace) ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;vdf -h 2&amp;gt;/dev/null | grep -i vsantrace || echo &amp;#34;(no vsantrace ramdisk line)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- top trace files by size ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;ls -lhS /vsantraces/ 2&amp;gt;/dev/null | head -n 15 || echo &amp;#34;(/vsantraces not present)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- recent ramdisk-full VOBs (last 20) ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;grep &amp;#34;vob.visorfs.ramdisk.full&amp;#34; /var/log/vobd.log 2&amp;gt;/dev/null | tail -n 20 || echo &amp;#34;(none)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Remediation A: restart vsantraced and show before/after ramdisk usage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -r -d &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; FIX_CMDS &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;===== $(hostname) =====&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- BEFORE: ramdisk usage ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;vdf -h 2&amp;gt;/dev/null | grep -i vsantrace || echo &amp;#34;(no vsantrace ramdisk line)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- restarting vsantraced ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;/etc/init.d/vsantraced restart 2&amp;gt;&amp;amp;1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;sleep 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- AFTER: ramdisk usage ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;vdf -h 2&amp;gt;/dev/null | grep -i vsantrace || echo &amp;#34;(no vsantrace ramdisk line)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Remediation B: delete OLD .zst archives, keeping the newest 3 per host.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Active (non-.zst) trace files are never touched.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -r -d &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; CLEAN_CMDS &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;===== $(hostname) =====&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- BEFORE: ramdisk usage ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;vdf -h 2&amp;gt;/dev/null | grep -i vsantrace || echo &amp;#34;(no vsantrace ramdisk line)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- deleting all but the newest 3 .zst archives ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;ls -t /vsantraces/*.zst 2&amp;gt;/dev/null | tail -n +4 | while read -r f; do
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; echo &amp;#34;rm $f&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; rm -f &amp;#34;$f&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;done
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;echo &amp;#34;--- AFTER: ramdisk usage ---&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;vdf -h 2&amp;gt;/dev/null | grep -i vsantrace || echo &amp;#34;(no vsantrace ramdisk line)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;run_on_host&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;cmds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTDIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; ssh &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_OPTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_USER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$cmds&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OK &lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="s2"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;FAIL &lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="s2"&gt; (see &lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt; for error)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fan_out&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;cmds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;pids&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; h in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; run_on_host &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$h&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$cmds&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$suffix&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;pids&lt;/span&gt;&lt;span class="o"&gt;+=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pids&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;confirm&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ASSUME_YES&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Proceed on all %d hosts? [y/N] &amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r reply
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$reply&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; in y&lt;span class="p"&gt;|&lt;/span&gt;Y&lt;span class="p"&gt;|&lt;/span&gt;yes&lt;span class="p"&gt;|&lt;/span&gt;YES&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; *&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Aborted.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# --- 1. QUERY (always) ------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Querying &lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; hosts in parallel as user &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$SSH_USER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Output dir: &lt;/span&gt;&lt;span class="nv"&gt;$OUTDIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fan_out &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$QUERY_CMDS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Hosts with the failover ramdisk at 100%:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -l &lt;span class="s2"&gt;&amp;#34;vsantraceFailover.*100%&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTDIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;/*.txt 2&amp;gt;/dev/null &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; (none — ramdisks have headroom)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# --- 2. FIX (only with --fix) -----------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DO_FIX&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; echo&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;FIX MODE: restart &amp;#39;vsantraced&amp;#39; on &lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; host(s).&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Only affects diagnostic tracing — no vSAN data, VMs, or I/O touched.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; confirm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; fan_out &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FIX_CMDS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-fix&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# --- 3. CLEAN (only with --clean) -------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DO_CLEAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; echo&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CLEAN MODE: delete old .zst archives on &lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; host(s).&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Keeps the newest 3 archives per host; active trace files untouched.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; confirm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; fan_out &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CLEAN_CMDS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-clean&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-1-run-the-query"&gt;Step 1: Run the Query&lt;/h3&gt;
&lt;p&gt;With your hostnames filled into &lt;code&gt;HOSTS_DEFAULT&lt;/code&gt; (or passed on the command line), run it with no arguments first. This is read-only and safe:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./query-vsan-traces.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You get one output file per host plus a summary line listing exactly which hosts are sitting at 100%. Confirm from the &lt;code&gt;esxcli vsan trace get&lt;/code&gt; output that the trace level is the default before going further — if it is, you know this is a stuck-ramdisk problem, not a misconfiguration.&lt;/p&gt;
&lt;h3 id="step-2-try-the-restart"&gt;Step 2: Try the Restart&lt;/h3&gt;
&lt;p&gt;The lightest-touch remediation is to restart the trace daemon, which re-initializes the trace directories:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./query-vsan-traces.sh --fix
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The script prints before/after &lt;code&gt;vdf&lt;/code&gt; output for each host. In my case the restart completed cleanly on all hosts but the failover ramdisk stayed at 100% — the daemon came back, but the files already on the ramdisk were not purged. That is the expected outcome when the ramdisk is already full, and it is exactly why the script does not stop here.&lt;/p&gt;
&lt;h3 id="step-3-reclaim-the-space"&gt;Step 3: Reclaim the Space&lt;/h3&gt;
&lt;p&gt;The reclaim step deletes the old rotated &lt;code&gt;.zst&lt;/code&gt; archives, which are what actually fills the ramdisk. The script keeps the newest three archives per host so you do not lose all recent history, and it never touches the active (non-&lt;code&gt;.zst&lt;/code&gt;) trace files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./query-vsan-traces.sh --clean
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each remediation mode prompts for confirmation once, listing the hosts it is about to act on, and you can skip the prompt with &lt;code&gt;--yes&lt;/code&gt; in an automation context. After the cleanup, the AFTER &lt;code&gt;vdf&lt;/code&gt; line should show the failover ramdisk back under capacity, and the &lt;code&gt;ramdisk.full&lt;/code&gt; VOBs should stop appearing within a minute or two.&lt;/p&gt;
&lt;h2 id="why-this-order-matters"&gt;Why This Order Matters&lt;/h2&gt;
&lt;p&gt;It is tempting to jump straight to deleting files, but running the query first buys you two things. You confirm the trace configuration is actually default (ruling out a real misconfiguration), and you capture a record of the pre-change state in the per-host output files. The restart is offered before the cleanup simply because it is the lower-impact action; when it does not free space, the cleanup is the definitive fix.&lt;/p&gt;
&lt;p&gt;If a host is &lt;em&gt;still&lt;/em&gt; at 100% after a cleanup, that points to something actively re-filling the ramdisk faster than rotation can drain it — a stuck writer, or a persistent trace target that is unwritable. At that point you are past the mechanical fixes and it is worth opening a support request rather than looping on the same commands.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The &amp;ldquo;vsantraceFailover ramdisk full&amp;rdquo; warning looks alarming because it repeats endlessly across every host, but it is a contained, diagnostic-only condition with a safe, repeatable fix. The key is to treat the whole cluster as a unit: query every host in parallel, confirm the configuration is default, attempt the cheap restart, and reclaim space by clearing old trace archives when the restart is not enough. A small Bash wrapper turns what would be a tedious host-by-host chore into three commands you can run from your workstation, with a written record of each host&amp;rsquo;s state along the way.&lt;/p&gt;</content:encoded></item><item><title>Fixing "Error attempting Upgrade Database Schema" When Upgrading vCenter 8.0 U3x to 9.1.0.0</title><link>https://cosmin.us/fixing-error-attempting-upgrade-database-schema-vcenter-9-1-0-0-upgrade/</link><pubDate>Tue, 16 Jun 2026 12:45:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/fixing-error-attempting-upgrade-database-schema-vcenter-9-1-0-0-upgrade/</guid><description>&lt;p&gt;If you&amp;rsquo;re moving vCenter Server from 8.0 U3x to 9.1.0.0 — typically as part of a &lt;a href="https://cosmin.us/deploy-a-vcf-9-instance-in-an-existing-vcf-fleet/"&gt;VCF 9.1 deployment&lt;/a&gt; or a VVF 9.1 upgrade — the run can fail partway through with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error attempting Upgrade Database Schema
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Please check vcintegrity migration logs for details.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is one of those failures that looks scary in the UI but has a clean, well-understood root cause: a handful of &lt;strong&gt;orphaned rows in the vcIntegrity database&lt;/strong&gt; that trip a new foreign-key constraint introduced in 9.1. Below is the whole story — what the error means, how to read the logs, why it happens, and how to clear it so the upgrade completes. It&amp;rsquo;s the same shape of problem I wrote about in &lt;a href="https://cosmin.us/fixing-postgresql-wont-start-after-reboot-on-vcf-operations-fleet-management-9-0-1-and-automating-it/"&gt;fixing PostgreSQL on Fleet Management 9.0.1&lt;/a&gt;: a schema change colliding with pre-existing data.&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you&amp;rsquo;re moving vCenter Server from 8.0 U3x to 9.1.0.0 — typically as part of a &lt;a href="https://cosmin.us/deploy-a-vcf-9-instance-in-an-existing-vcf-fleet/"&gt;VCF 9.1 deployment&lt;/a&gt; or a VVF 9.1 upgrade — the run can fail partway through with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error attempting Upgrade Database Schema
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Please check vcintegrity migration logs for details.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is one of those failures that looks scary in the UI but has a clean, well-understood root cause: a handful of &lt;strong&gt;orphaned rows in the vcIntegrity database&lt;/strong&gt; that trip a new foreign-key constraint introduced in 9.1. Below is the whole story — what the error means, how to read the logs, why it happens, and how to clear it so the upgrade completes. It&amp;rsquo;s the same shape of problem I wrote about in &lt;a href="https://cosmin.us/fixing-postgresql-wont-start-after-reboot-on-vcf-operations-fleet-management-9-0-1-and-automating-it/"&gt;fixing PostgreSQL on Fleet Management 9.0.1&lt;/a&gt;: a schema change colliding with pre-existing data.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is documented in &lt;strong&gt;Broadcom KB 442223&lt;/strong&gt;. The supported fix uses a script attached to that KB. I&amp;rsquo;ll walk through the official steps first, then share an &lt;em&gt;unofficial&lt;/em&gt; helper of my own for the curious — clearly marked, and not a replacement for the KB script.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="the-symptom"&gt;The symptom&lt;/h2&gt;
&lt;p&gt;The upgrade proceeds normally until the &lt;strong&gt;vcIntegrity import phase&lt;/strong&gt;, then halts with the generic schema error shown above. The UI doesn&amp;rsquo;t tell you &lt;em&gt;what&lt;/em&gt; in the schema failed — it just points you at the vcIntegrity migration logs. That&amp;rsquo;s your cue to SSH in and read the actual exception.&lt;/p&gt;
&lt;!-- TODO (optional): add the error screenshot from KB 442223 here.
 Save it to static/images/2026/06/vcenter-911-upgrade-db-schema-error.webp and uncomment:
![vCenter upgrade failing during the vcIntegrity import phase with "Error attempting Upgrade Database Schema".](/images/2026/06/vcenter-911-upgrade-db-schema-error.webp)
--&gt;
&lt;hr&gt;
&lt;h2 id="reading-the-upgrade-log"&gt;Reading the upgrade log&lt;/h2&gt;
&lt;p&gt;On the source vCenter, open the vcIntegrity import log:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/var/log/vmware/upgrade/Import_com.vmware.vcIntegrity_####_##_##_##_##.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The meaningful lines look like this (timestamps and IDs trimmed):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DEBUG vmware-vci-vciInstallUtils [VdbODBCError::VdbODBCError] SQLGetDiagRec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; SQLState=&amp;#39;23503&amp;#39; NativeError=1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; MessageText=&amp;#39;ERROR: insert or update on table &amp;#34;pm_recommendation_spec&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; violates foreign key constraint &amp;#34;fk_pm_recommendation_spec&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; DETAIL: Key (entity_id, spec_id)=(domain-##, -1000) is not present in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; table &amp;#34;pm_recommendation_info&amp;#34;.&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR vmware-vci-vciInstallUtils [installerRunDBCommand 626] [DATABASE]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Vdb::RunScript, SQL Exec Error: &amp;#34;ODBC error: (23503) - ERROR: insert or
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; update on table &amp;#34;pm_recommendation_spec&amp;#34; violates foreign key constraint
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;fk_pm_recommendation_spec&amp;#34; ... &amp;#34; is returned when executing SQL statement
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;ALTER TABLE IF EXISTS PM_RECOMMENDATION_SPEC
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ADD CONSTRAINT FK_PM_RECOMMENDATION_SPEC
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; FOREIGN KEY (ENTITY_ID, SPEC_ID)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; REFERENCES PM_RECOMMENDATION_INFO (ENTITY_ID, SPEC_ID)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ON DELETE CASCADE&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR __main__ Upgrade Phase &amp;#39;vcIntegrity:Import&amp;#39; failed.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Exception: Error attempting Upgrade Database Schema
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upgrade_errors.PermanentError: Error attempting Upgrade Database Schema
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Three things are worth pulling out of that wall of text:&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;SQLState is &lt;code&gt;23503&lt;/code&gt;&lt;/strong&gt;, which in PostgreSQL is &lt;code&gt;foreign_key_violation&lt;/code&gt; — not a connectivity, permissions, or disk problem. The database is doing exactly what it was told to do and refusing bad data.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;failing statement is an &lt;code&gt;ALTER TABLE ... ADD CONSTRAINT&lt;/code&gt;&lt;/strong&gt;, not an insert from your workload. The migration is trying to &lt;em&gt;add&lt;/em&gt; the new constraint &lt;code&gt;FK_PM_RECOMMENDATION_SPEC&lt;/code&gt; to an existing table, and Postgres validates the rows already in that table before it will accept the constraint.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;offending key is &lt;code&gt;(entity_id, spec_id) = (domain-##, -1000)&lt;/code&gt;&lt;/strong&gt;. There&amp;rsquo;s a row in &lt;code&gt;pm_recommendation_spec&lt;/code&gt; whose &lt;code&gt;(entity_id, spec_id)&lt;/code&gt; pair has no matching parent row in &lt;code&gt;pm_recommendation_info&lt;/code&gt;. The &lt;code&gt;-1000&lt;/code&gt; is a sentinel/placeholder value, and &lt;code&gt;domain-##&lt;/code&gt; is a managed-object reference — classic leftovers from objects that were removed or never fully written.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="root-cause"&gt;Root cause&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;pm_recommendation_*&lt;/code&gt; tables live in the &lt;strong&gt;vcIntegrity&lt;/strong&gt; database — the component behind vSphere Lifecycle Manager / Update Planner. They store lifecycle &amp;ldquo;recommendation&amp;rdquo; data: &lt;code&gt;pm_recommendation_info&lt;/code&gt; holds the parent records, and &lt;code&gt;pm_recommendation_spec&lt;/code&gt; holds child spec rows that are supposed to reference a parent.&lt;/p&gt;
&lt;p&gt;Over the life of an 8.0 environment, it&amp;rsquo;s possible to end up with &lt;strong&gt;child spec rows whose parent info row is gone&lt;/strong&gt; (or was never created) — for example after domains/clusters are removed, or from older code paths that didn&amp;rsquo;t clean up specs. In 8.0 there was no enforced relationship between the two tables, so these orphans sat there harmlessly.&lt;/p&gt;
&lt;p&gt;vCenter &lt;strong&gt;9.1.0.0 hardens the schema&lt;/strong&gt;. The migration adds a composite foreign key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM_RECOMMENDATION_SPEC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FK_PM_RECOMMENDATION_SPEC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENTITY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SPEC_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PM_RECOMMENDATION_INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENTITY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SPEC_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When PostgreSQL adds a foreign key, it &lt;strong&gt;validates every existing row&lt;/strong&gt; in the child table against the parent table. Any orphan — a &lt;code&gt;(entity_id, spec_id)&lt;/code&gt; pair with no parent — violates the new constraint, so the engine aborts the &lt;code&gt;ALTER TABLE&lt;/code&gt; with error &lt;code&gt;23503&lt;/code&gt;. Because the migration treats this as a &lt;code&gt;PermanentError&lt;/code&gt;, the whole vcIntegrity import phase fails and the upgrade stops.&lt;/p&gt;
&lt;p&gt;In short: &lt;strong&gt;9.1 is correct to demand referential integrity; your 8.0 data just doesn&amp;rsquo;t satisfy it yet.&lt;/strong&gt; The fix is to remove the orphaned spec rows so the constraint can be created cleanly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Broadcom notes that a fix to prevent this will be included in a future VCF release. Until then, the workaround below is the path forward.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="the-official-fix-kb-442223-workaround"&gt;The official fix (KB 442223 workaround)&lt;/h2&gt;
&lt;p&gt;Do this on the &lt;strong&gt;source&lt;/strong&gt; vCenter appliance, before retrying the upgrade.&lt;/p&gt;
&lt;h3 id="1-snapshot-the-vcenter-appliance"&gt;1. Snapshot the vCenter appliance&lt;/h3&gt;
&lt;p&gt;Take a snapshot of the source vCenter VM first. Editing the embedded database is low-risk when done correctly, but a snapshot is your guaranteed rollback. (If your environment enforces a snapshot-age policy, see &lt;a href="https://cosmin.us/change-delete-old-snapshot-restriction-from-7-days/"&gt;changing the old-snapshot restriction&lt;/a&gt; — just remember to remove this snapshot once the upgrade succeeds.)&lt;/p&gt;
&lt;h3 id="2-download-the-cleanup-script-from-the-kb"&gt;2. Download the cleanup script from the KB&lt;/h3&gt;
&lt;p&gt;Download &lt;code&gt;cleanup_rec_orphans.py&lt;/code&gt; from the &lt;strong&gt;Attachments&lt;/strong&gt; section of &lt;a href="https://knowledge.broadcom.com/external/article/442223"&gt;KB 442223&lt;/a&gt;. This is the supported script — use it for production work.&lt;/p&gt;
&lt;h3 id="3-copy-the-script-to-the-appliance"&gt;3. Copy the script to the appliance&lt;/h3&gt;
&lt;p&gt;Place it in &lt;code&gt;/tmp&lt;/code&gt; on the vCenter server. You can use an SCP client such as WinSCP. If WinSCP fails with a &lt;em&gt;&amp;ldquo;Received too large … SFTP packet&amp;rdquo;&lt;/em&gt; error, follow &lt;a href="https://knowledge.broadcom.com/external/article/326317/connecting-to-vcenter-server-virtual-app.html"&gt;KB 326317&lt;/a&gt; (set the shell to &lt;code&gt;bash&lt;/code&gt; for the session, or use the SCP fallback).&lt;/p&gt;
&lt;h3 id="4-ssh-in-as-root"&gt;4. SSH in as root&lt;/h3&gt;
&lt;p&gt;Log in to the vCenter appliance over SSH with &lt;code&gt;root&lt;/code&gt;. Enable the shell if needed (&lt;code&gt;shell&lt;/code&gt; from the appliancesh prompt).&lt;/p&gt;
&lt;h3 id="5-dry-run-audit-read-only"&gt;5. Dry-run audit (read-only)&lt;/h3&gt;
&lt;p&gt;Run the script with no flags first. It reports orphans &lt;strong&gt;without changing anything&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su updatemgr -s /bin/bash -c &lt;span class="s2"&gt;&amp;#34;python3 /tmp/cleanup_rec_orphans.py&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;su updatemgr&lt;/code&gt; matters: the &lt;code&gt;updatemgr&lt;/code&gt; account owns the vcIntegrity database connection, so the script authenticates correctly without you handling credentials.&lt;/p&gt;
&lt;h3 id="6-execute-the-cleanup"&gt;6. Execute the cleanup&lt;/h3&gt;
&lt;p&gt;Once you&amp;rsquo;ve reviewed the dry-run output, run it again with &lt;code&gt;--execute&lt;/code&gt; to delete the orphaned rows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su updatemgr -s /bin/bash -c &lt;span class="s2"&gt;&amp;#34;python3 /tmp/cleanup_rec_orphans.py --execute&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="7-retry-the-upgrade"&gt;7. Retry the upgrade&lt;/h3&gt;
&lt;p&gt;Re-run the vCenter upgrade. With the orphans gone, the &lt;code&gt;ALTER TABLE ... ADD CONSTRAINT&lt;/code&gt; validates cleanly and the vcIntegrity import phase completes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-the-script-actually-does-the-sql-behind-it"&gt;What the script actually does (the SQL behind it)&lt;/h2&gt;
&lt;p&gt;You don&amp;rsquo;t need to know this to run the KB script, but it helps to understand &lt;em&gt;why&lt;/em&gt; it&amp;rsquo;s safe. The job is purely to find child rows with no parent and remove them. The detection is a straightforward anti-join:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- Orphans: spec rows whose (entity_id, spec_id) has no parent info row
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spec_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pm_recommendation_spec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pm_recommendation_info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spec_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spec_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spec_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the cleanup is the matching delete:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pm_recommendation_spec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pm_recommendation_info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spec_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spec_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. You&amp;rsquo;re deleting only spec rows that already point at nothing — exactly the rows the new constraint would reject. No valid recommendation data is touched.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="optional-an-unofficial-auditcleanup-helper"&gt;Optional: an unofficial audit/cleanup helper&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Use the KB script for anything you care about.&lt;/strong&gt; The Bash helper below is a community convenience I wrote to mirror the same logic for quick auditing in labs. It is &lt;strong&gt;unofficial and unsupported&lt;/strong&gt;, runs &lt;strong&gt;read-only by default&lt;/strong&gt;, and should only be pointed at a vCenter you&amp;rsquo;ve snapshotted. If in doubt, stop and use &lt;code&gt;cleanup_rec_orphans.py&lt;/code&gt; from the KB.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It does three things: locate the database that actually owns the table (so it doesn&amp;rsquo;t matter what the DB is named in your build), report the orphan count and the offending pairs, and — only with &lt;code&gt;--execute&lt;/code&gt; — delete them inside a transaction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# audit_rec_orphans.sh — UNOFFICIAL community helper (KB 442223)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Mirrors the intent of Broadcom&amp;#39;s cleanup_rec_orphans.py: find/remove&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# orphaned rows in pm_recommendation_spec that have no parent in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# pm_recommendation_info and therefore block the FK_PM_RECOMMENDATION_SPEC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# constraint added by the vCenter 9.1.0.0 vcIntegrity schema upgrade.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# DRY-RUN by default. Pass --execute to delete.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ALWAYS snapshot the vCenter appliance first.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Prefer the official KB script for supported/production environments.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run it the same way the KB runs its script (as the vcIntegrity DB owner):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# su updatemgr -s /bin/bash -c &amp;#34;/tmp/audit_rec_orphans.sh&amp;#34; # audit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# su updatemgr -s /bin/bash -c &amp;#34;/tmp/audit_rec_orphans.sh --execute&amp;#34; # delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;EXECUTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--execute&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;EXECUTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;id -un&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;updatemgr&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[!] Not running as &amp;#39;updatemgr&amp;#39; — DB auth will likely fail.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; Re-run: su updatemgr -s /bin/bash -c \&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Prefer the embedded vPostgres client if present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PSQL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/vmware/vpostgres/current/bin/psql&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; -x &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PSQL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;PSQL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; -v psql&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Discover which database owns pm_recommendation_spec (override with VCI_DB=...)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;VCI_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VCI_DB&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCI_DB&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; db in &lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PSQL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -Atqc &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;SELECT datname FROM pg_database WHERE datistemplate=false&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PSQL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -Atqd &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c &lt;span class="s2"&gt;&amp;#34;SELECT to_regclass(&amp;#39;pm_recommendation_spec&amp;#39;)&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;VCI_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCI_DB&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[x] Could not locate the vcIntegrity DB (set VCI_DB=...)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[*] Using database: &lt;/span&gt;&lt;span class="nv"&gt;$VCI_DB&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ORPHAN_FILTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;FROM pm_recommendation_spec s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; LEFT JOIN pm_recommendation_info i
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; ON i.entity_id = s.entity_id AND i.spec_id = s.spec_id
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; WHERE i.entity_id IS NULL&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PSQL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -Atqd &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCI_DB&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -c &lt;span class="s2"&gt;&amp;#34;SELECT count(*) &lt;/span&gt;&lt;span class="nv"&gt;$ORPHAN_FILTER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[*] Orphaned pm_recommendation_spec rows: &lt;/span&gt;&lt;span class="nv"&gt;$COUNT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$COUNT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[+] No orphans — FK_PM_RECOMMENDATION_SPEC will validate cleanly.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[*] Offending (entity_id, spec_id) pairs:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PSQL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -d &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCI_DB&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -c &lt;span class="s2"&gt;&amp;#34;SELECT s.entity_id, s.spec_id &lt;/span&gt;&lt;span class="nv"&gt;$ORPHAN_FILTER&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; ORDER BY s.entity_id, s.spec_id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$EXECUTE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -ne &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[i] DRY-RUN only. Re-run with --execute to delete the rows above.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[*] Deleting orphaned rows in a transaction…&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PSQL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -v &lt;span class="nv"&gt;ON_ERROR_STOP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; -d &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VCI_DB&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;&amp;lt;&amp;#39;SQL&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;BEGIN;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;DELETE FROM pm_recommendation_spec s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;WHERE NOT EXISTS (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; SELECT 1 FROM pm_recommendation_info i
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; WHERE i.entity_id = s.entity_id AND i.spec_id = s.spec_id
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;COMMIT;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[+] Done. Re-run without --execute to confirm 0 orphans, then retry the upgrade.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save it to &lt;code&gt;/tmp/audit_rec_orphans.sh&lt;/code&gt;, then run the dry-run first and review the output before ever adding &lt;code&gt;--execute&lt;/code&gt;. Like the KB script, it&amp;rsquo;s idempotent — running it again after a successful cleanup simply reports zero orphans.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="verifying-the-fix"&gt;Verifying the fix&lt;/h2&gt;
&lt;p&gt;Re-run the audit (dry-run) — it should now report &lt;strong&gt;0 orphaned rows&lt;/strong&gt;. That single number is the whole signal: zero orphans means the new foreign key has nothing left to reject.&lt;/p&gt;
&lt;p&gt;Then retry the vCenter upgrade and confirm the &lt;strong&gt;vcIntegrity:Import&lt;/strong&gt; phase passes. If you want belt-and-suspenders confirmation, tail the new import log and check that the &lt;code&gt;ALTER TABLE ... ADD CONSTRAINT FK_PM_RECOMMENDATION_SPEC&lt;/code&gt; statement executes without a &lt;code&gt;23503&lt;/code&gt; error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -i &lt;span class="s2"&gt;&amp;#34;FK_PM_RECOMMENDATION_SPEC\|23503\|Upgrade Database Schema&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /var/log/vmware/upgrade/Import_com.vmware.vcIntegrity_*.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No matches on &lt;code&gt;23503&lt;/code&gt; (and a clean run past the schema step) means you&amp;rsquo;re through it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="rollback"&gt;Rollback&lt;/h2&gt;
&lt;p&gt;If anything looks wrong after the cleanup or the retried upgrade, &lt;strong&gt;revert to the snapshot&lt;/strong&gt; you took in step 1 and reassess before trying again. The cleanup itself only removes already-orphaned rows, so a revert puts you exactly back where you started.&lt;/p&gt;
&lt;p&gt;Once the upgrade has completed successfully and vCenter is healthy, &lt;strong&gt;delete the snapshot&lt;/strong&gt; — leaving snapshots on a vCenter appliance long-term causes its own problems (disk growth, performance, and consolidation headaches).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="notes--gotchas"&gt;Notes &amp;amp; gotchas&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;su updatemgr&lt;/code&gt;?&lt;/strong&gt; That OS account maps to the database role that owns the vcIntegrity connection. Running the script under it means you never handle DB credentials by hand, and the connection has exactly the rights it needs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is deleting these rows safe?&lt;/strong&gt; Yes — you&amp;rsquo;re only removing &lt;code&gt;pm_recommendation_spec&lt;/code&gt; rows that already reference a non-existent parent. They&amp;rsquo;re dead lifecycle-recommendation data; no live configuration depends on them. Recommendations regenerate as needed after the upgrade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does it apply to VVF as well as VCF?&lt;/strong&gt; The KB lists both VMware Cloud Foundation 9.1 and VMware vSphere Foundation 9.1 — the trigger is the vCenter 9.1.0.0 schema change, so it&amp;rsquo;s the same regardless of which edition you&amp;rsquo;re upgrading under.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Will it come back?&lt;/strong&gt; Broadcom has stated a fix is planned for a future VCF release so the orphans don&amp;rsquo;t block the migration. Until then, audit-then-clean is the routine.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/442223"&gt;Broadcom KB 442223 — &amp;ldquo;Error attempting Upgrade Database Schema&amp;rdquo; during vCenter 8.0 U3x to 9.1.0.0 upgrade&lt;/a&gt; (symptoms, cause, and the official &lt;code&gt;cleanup_rec_orphans.py&lt;/code&gt; workaround).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/326317/connecting-to-vcenter-server-virtual-app.html"&gt;Broadcom KB 326317 — Connecting to vCenter Server Virtual Appliance using WinSCP fails with &amp;ldquo;Received too large … SFTP packet&amp;rdquo;&lt;/a&gt; (needed if file transfer to the appliance fails).&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Fix: Unable to Update BOM Components — "Compatibility File Is Missing" in SDDC Manager</title><link>https://cosmin.us/unable-to-update-bom-components-from-sddc-manager-compatibility-file-is-missing/</link><pubDate>Fri, 05 Jun 2026 09:15:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/unable-to-update-bom-components-from-sddc-manager-compatibility-file-is-missing/</guid><description>&lt;p&gt;If you have &lt;a href="https://cosmin.us/upgrading-vcf-4-3-x-to-vcf-5-step-by-step/"&gt;upgraded your VMware Cloud Foundation (VCF) environment to version 5.x&lt;/a&gt; from an earlier release, you may find yourself unable to update BOM (Bill of Materials) components from the SDDC Manager UI. The &lt;strong&gt;&amp;ldquo;Update Now&amp;rdquo;&lt;/strong&gt; or &lt;strong&gt;&amp;ldquo;Configure Update&amp;rdquo;&lt;/strong&gt; button is greyed out and you see a frustrating error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Update cannot be started now as compatibility file is missing&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post walks through the symptoms, root cause, and the step-by-step fix based on &lt;a href="https://knowledge.broadcom.com/external/article/396202/unable-to-update-bom-components-from-the.html"&gt;Broadcom KB 396202&lt;/a&gt;.&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you have &lt;a href="https://cosmin.us/upgrading-vcf-4-3-x-to-vcf-5-step-by-step/"&gt;upgraded your VMware Cloud Foundation (VCF) environment to version 5.x&lt;/a&gt; from an earlier release, you may find yourself unable to update BOM (Bill of Materials) components from the SDDC Manager UI. The &lt;strong&gt;&amp;ldquo;Update Now&amp;rdquo;&lt;/strong&gt; or &lt;strong&gt;&amp;ldquo;Configure Update&amp;rdquo;&lt;/strong&gt; button is greyed out and you see a frustrating error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;Update cannot be started now as compatibility file is missing&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post walks through the symptoms, root cause, and the step-by-step fix based on &lt;a href="https://knowledge.broadcom.com/external/article/396202/unable-to-update-bom-components-from-the.html"&gt;Broadcom KB 396202&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="symptoms"&gt;Symptoms&lt;/h3&gt;
&lt;p&gt;You will typically notice one or more of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Updates&lt;/strong&gt; tab of the relevant Workload Domain (WLD), the &lt;strong&gt;&amp;ldquo;Update Now&amp;rdquo;&lt;/strong&gt; or &lt;strong&gt;&amp;ldquo;Configure Update&amp;rdquo;&lt;/strong&gt; button is greyed out.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&amp;ldquo;Available Updates&amp;rdquo;&lt;/strong&gt; section displays the error: &lt;em&gt;&amp;ldquo;Update cannot be started now as compatibility file is missing&amp;rdquo;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Attempting to manually download the &lt;code&gt;VmwareCompatibilityData.json&lt;/code&gt; file fails.&lt;/li&gt;
&lt;li&gt;In the SDDC Manager LCM debug log (&lt;code&gt;/var/log/vmware/vcf/lcm/lcm-debug.log&lt;/code&gt;), you see entries similar to:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Recoverable I/O exception (java.net.SocketException) caught when processing request to {s}-&amp;gt;https://storage.googleapis.com:443
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;VVS Compatibility Data update failed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;com.vmware.vcf.compatibility.controllers.internal.vvs.exception.VvsException:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;The VVS api call to https://vvs.esp.vmware.com/v1/products/bundles/type/vcf-lcm-bundle?format=json failed.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Response: Connection reset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="root-cause"&gt;Root Cause&lt;/h3&gt;
&lt;p&gt;The compatibility data file &lt;code&gt;VmwareCompatibilityData.json&lt;/code&gt; is missing from the expected path on the SDDC Manager appliance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/nfs/vmware/vcf/nfs-mount/compatibility/VmwareCompatibilityData.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Why is it missing?&lt;/strong&gt; The most common reason is that the SDDC Manager was upgraded from a VCF version prior to 5.0.0.0. The VVS compatibility data directory was introduced in VCF 5.0, so environments that were upgraded (rather than freshly deployed) may never have had this directory created.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="step-by-step-resolution"&gt;Step-by-Step Resolution&lt;/h3&gt;
&lt;h4 id="step-1--take-a-snapshot"&gt;Step 1 — Take a Snapshot&lt;/h4&gt;
&lt;p&gt;Before making any changes, take an &lt;strong&gt;offline snapshot&lt;/strong&gt; of the SDDC Manager appliance VM. This gives you a safe rollback point.&lt;/p&gt;
&lt;h4 id="step-2--ssh-into-sddc-manager"&gt;Step 2 — SSH Into SDDC Manager&lt;/h4&gt;
&lt;p&gt;Connect via SSH using the &lt;code&gt;vcf&lt;/code&gt; user, then elevate to root:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh vcf@&amp;lt;sddc-manager-fqdn&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su root
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="step-3--create-the-compatibility-directory"&gt;Step 3 — Create the Compatibility Directory&lt;/h4&gt;
&lt;p&gt;The directory likely does not exist. Create it and navigate into it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p /nfs/vmware/vcf/nfs-mount/compatibility/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /nfs/vmware/vcf/nfs-mount/compatibility/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="step-4--download-the-compatibility-data-file"&gt;Step 4 — Download the Compatibility Data File&lt;/h4&gt;
&lt;p&gt;Run the following &lt;code&gt;curl&lt;/code&gt; command to pull the &lt;code&gt;VmwareCompatibilityData.json&lt;/code&gt; file from Broadcom&amp;rsquo;s VVS endpoint:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl --location &lt;span class="s1"&gt;&amp;#39;https://vvs.broadcom.com/v1/products/bundles/type/vcf-lcm-v2-bundle?format=json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --header &lt;span class="s1"&gt;&amp;#39;X-Vmw-Esp-ClientId: vcf-lcm&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt; VmwareCompatibilityData.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If the SDDC Manager does not have internet access, run the &lt;code&gt;curl&lt;/code&gt; command on a separate Linux machine that can reach &lt;code&gt;vvs.broadcom.com&lt;/code&gt;, then transfer the resulting &lt;code&gt;VmwareCompatibilityData.json&lt;/code&gt; file to the SDDC Manager appliance via SCP or another file transfer method.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="step-5--set-correct-permissions-and-ownership"&gt;Step 5 — Set Correct Permissions and Ownership&lt;/h4&gt;
&lt;p&gt;Apply the proper ownership and permissions so the LCM service can read the file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chown vcf_lcm:vcf -R /nfs/vmware/vcf/nfs-mount/compatibility
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod &lt;span class="m"&gt;755&lt;/span&gt; -R /nfs/vmware/vcf/nfs-mount/compatibility/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="step-6--restart-the-lcm-service"&gt;Step 6 — Restart the LCM Service&lt;/h4&gt;
&lt;p&gt;Restart the Lifecycle Manager service for the changes to take effect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl restart lcm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wait a couple of minutes for the service to fully come back up, then refresh the SDDC Manager UI. The &lt;strong&gt;&amp;ldquo;Update Now&amp;rdquo;&lt;/strong&gt; or &lt;strong&gt;&amp;ldquo;Configure Update&amp;rdquo;&lt;/strong&gt; button should no longer be greyed out.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="additional-step--update-depot-urls"&gt;Additional Step — Update Depot URLs&lt;/h3&gt;
&lt;p&gt;If you are also seeing depot-related errors, the download URLs in the LCM configuration may still reference old repository endpoints that require a valid download token. You should update the depot configuration in:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/opt/vmware/vcf/lcm/lcm-app/conf/application-prod.properties
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Broadcom has a dedicated KB for this issue: &lt;a href="https://knowledge.broadcom.com/external/article/389871"&gt;Depot connection has issues — SDDC fails to download updates from online repositories (KB 389871)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On a related note, if compatibility or interoperability checks are blocking upgrades in your lab, I covered a &lt;a href="https://cosmin.us/unstuck-your-vcf-lab-a-script-to-bypass-compatibility-interoperability-checks/"&gt;script to bypass VCF compatibility and interoperability checks&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="quick-reference--all-commands"&gt;Quick Reference — All Commands&lt;/h3&gt;
&lt;p&gt;For convenience, here are all the commands in sequence:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SSH in and elevate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh vcf@&amp;lt;sddc-manager-fqdn&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su root
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Create directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p /nfs/vmware/vcf/nfs-mount/compatibility/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /nfs/vmware/vcf/nfs-mount/compatibility/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Download compatibility data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl --location &lt;span class="s1"&gt;&amp;#39;https://vvs.broadcom.com/v1/products/bundles/type/vcf-lcm-v2-bundle?format=json&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --header &lt;span class="s1"&gt;&amp;#39;X-Vmw-Esp-ClientId: vcf-lcm&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt; VmwareCompatibilityData.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Fix permissions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chown vcf_lcm:vcf -R /nfs/vmware/vcf/nfs-mount/compatibility
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod &lt;span class="m"&gt;755&lt;/span&gt; -R /nfs/vmware/vcf/nfs-mount/compatibility/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Restart LCM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl restart lcm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="related-resources"&gt;Related Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/396202/unable-to-update-bom-components-from-the.html"&gt;Broadcom KB 396202 — Unable to update BOM components from SDDC Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/389871"&gt;Broadcom KB 389871 — Depot connection issues / Invalid credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/327186/public-url-list-for-sddc-manager.html"&gt;Public URL list for VCF Products (KB 327186)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/390098#download_token"&gt;VCF authenticated downloads configuration update instructions (KB 390098)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Scripted Components Cleanup from VCF Operations 9.1: A Step-by-Step Guide</title><link>https://cosmin.us/scripted-components-cleanup-from-vcf-operations-9-1/</link><pubDate>Wed, 03 Jun 2026 10:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/scripted-components-cleanup-from-vcf-operations-9-1/</guid><description>&lt;p&gt;If you&amp;rsquo;ve been working with VCF Operations 9.1, you may have noticed that on the &lt;strong&gt;Build&lt;/strong&gt; &amp;gt; &lt;strong&gt;Lifecycle&lt;/strong&gt; &amp;gt; &lt;strong&gt;VCF Management&lt;/strong&gt; &amp;gt; &lt;strong&gt;Components&lt;/strong&gt; page, manually-added components such as VCF Operations for Networks, Log Management, Real-Time Metrics or VCF Automation no longer have a &amp;ldquo;Delete&amp;rdquo; option available in the UI.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://cosmin.us/deploying-vcf-operations-9-from-ova/"&gt;VCF Operations 9.0&lt;/a&gt;, this was possible on the &lt;strong&gt;Fleet Management&lt;/strong&gt; &amp;gt; &lt;strong&gt;Lifecycle&lt;/strong&gt; &amp;gt; &lt;strong&gt;Components&lt;/strong&gt; page by clicking the three vertical dots next to a component and selecting &lt;strong&gt;Delete&lt;/strong&gt;. That option is gone in 9.1.&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you&amp;rsquo;ve been working with VCF Operations 9.1, you may have noticed that on the &lt;strong&gt;Build&lt;/strong&gt; &amp;gt; &lt;strong&gt;Lifecycle&lt;/strong&gt; &amp;gt; &lt;strong&gt;VCF Management&lt;/strong&gt; &amp;gt; &lt;strong&gt;Components&lt;/strong&gt; page, manually-added components such as VCF Operations for Networks, Log Management, Real-Time Metrics or VCF Automation no longer have a &amp;ldquo;Delete&amp;rdquo; option available in the UI.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://cosmin.us/deploying-vcf-operations-9-from-ova/"&gt;VCF Operations 9.0&lt;/a&gt;, this was possible on the &lt;strong&gt;Fleet Management&lt;/strong&gt; &amp;gt; &lt;strong&gt;Lifecycle&lt;/strong&gt; &amp;gt; &lt;strong&gt;Components&lt;/strong&gt; page by clicking the three vertical dots next to a component and selecting &lt;strong&gt;Delete&lt;/strong&gt;. That option is gone in 9.1.&lt;/p&gt;
&lt;p&gt;Broadcom has published &lt;a href="https://knowledge.broadcom.com/external/article/441333"&gt;KB article 441333&lt;/a&gt; with an attached Python script (&lt;code&gt;cleanup_component.py&lt;/code&gt;) that fills this gap. In this post I walk through how to use it for each component type.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before running the cleanup script, make sure the following are in place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt; installed on the system where you will run the script (can be any system with network access to the Fleet and VCF Management Services components).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network access&lt;/strong&gt; to the &lt;a href="https://cosmin.us/deploying-vcf-fleet-management-9-from-ova-a-step-by-step-guide/"&gt;Fleet LCM appliance&lt;/a&gt; and VCF Services Runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Credentials&lt;/strong&gt; — use username &lt;code&gt;admin&lt;/code&gt; as the value for the VCF Services Runtime username. The password is the same as the &lt;code&gt;vmware-system-user&lt;/code&gt; password.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FQDN values&lt;/strong&gt; — replace all placeholder FQDNs such as &lt;code&gt;&amp;lt;fleet-lcm-fqdn&amp;gt;&lt;/code&gt; with the actual values. These can be obtained from &lt;strong&gt;VCF Operations UI&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Build&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Lifecycle&lt;/strong&gt; -&amp;gt; &lt;strong&gt;VCF Management&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Components&lt;/strong&gt; (e.g., &lt;code&gt;vsp01.example.com&lt;/code&gt; for &lt;code&gt;&amp;lt;vcf-services-runtime-fqdn&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Prior to deletion, verify that you have a backup of any critical component data which needs to be retained, such as logs or database backups.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="download-and-verify-the-script"&gt;Download and Verify the Script&lt;/h2&gt;
&lt;p&gt;Download &lt;code&gt;cleanup_component.py&lt;/code&gt; from &lt;a href="https://knowledge.broadcom.com/external/article/441333"&gt;KB 441333&lt;/a&gt; (attached at the bottom of the article).&lt;/p&gt;
&lt;p&gt;Verify the SHA256 checksum before running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256sum cleanup_component.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Expected checksum:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;27A82D393333D17261EF1E8629A69AD6C40659A8537DF961DE95E545FE656F5B
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="what-the-script-does"&gt;What the Script Does&lt;/h2&gt;
&lt;p&gt;The LCM component cleanup script enables administrators to manage Day-N deployed components visible in the VCF Operations UI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;List components&lt;/strong&gt; — View all deployed components currently eligible for removal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delete components&lt;/strong&gt; — Safely remove inoperable or misconfigured components from the system so they can be redeployed later through VCF Operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Important notes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a component is running a version older than 9.1, it is removed solely from the Fleet lifecycle. The script does not delete the corresponding infrastructure deployments such as vCenter or VCF Services Runtime.&lt;/li&gt;
&lt;li&gt;VSP refers to VCF Management Services.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="1-ova-components"&gt;1. OVA Components&lt;/h2&gt;
&lt;p&gt;Deleting an OVA-based deployment removes it from Fleet lifecycle and also deletes the associated virtual machines from vCenter.&lt;/p&gt;
&lt;h3 id="list-components-deployed-on-vcenter"&gt;List Components Deployed on vCenter&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py list ova-component --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="delete-component-deployed-on-vcenter"&gt;Delete Component Deployed on vCenter&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py delete ova-component --component-id &amp;lt;ova-component-id&amp;gt; --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt; --vcenter-username &amp;lt;vcenter-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The script will prompt for the required passwords. Once confirmed, the OVA component will be removed from Fleet lifecycle and its associated VMs deleted from vCenter.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="2-vcf-management-services-vsp-components"&gt;2. VCF Management Services (VSP) Components&lt;/h2&gt;
&lt;p&gt;Deleting a VSP-based deployment removes it from Fleet lifecycle and also deletes the associated deployment from VCF Services Runtime.&lt;/p&gt;
&lt;h3 id="list-components-deployed-on-vcf-services-runtime"&gt;List Components Deployed on VCF Services Runtime&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py list vsp-component --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="delete-component-deployed-on-vcf-services-runtime"&gt;Delete Component Deployed on VCF Services Runtime&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py delete vsp-component --component-id &amp;lt;vsp-component-id&amp;gt; --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="3-vcf-automation"&gt;3. VCF Automation&lt;/h2&gt;
&lt;p&gt;VCF Automation is a multi-component deployment consisting of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VCF Automation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration Service Engine&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VCF Automation VCF Services Runtime&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; You must remove VCF Automation and the Migration Service Engine &lt;strong&gt;before&lt;/strong&gt; you remove the VCF Automation VCF Services Runtime. Follow the order below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="step-1-remove-vcf-automation-and-migration-service-engine"&gt;Step 1: Remove VCF Automation and Migration Service Engine&lt;/h3&gt;
&lt;h4 id="list-components-deployed-on-vcf-services-runtime-1"&gt;List Components Deployed on VCF Services Runtime&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py list vsp-component --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="delete-component-deployed-on-the-vcf-automation-vcf-services-runtime"&gt;Delete Component Deployed on the VCF Automation VCF Services Runtime&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py delete vsp-component --component-id &amp;lt;vsp-component-id&amp;gt; --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt; --vcfa-vcf-services-runtime-fqdn &amp;lt;vcfa-vcf-services-runtime-fqdn&amp;gt; --vcfa-vcf-services-runtime-username &amp;lt;vcfa-vcf-services-runtime-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run the delete command for both the VCF Automation component and the Migration Service Engine component before proceeding to Step 2.&lt;/p&gt;
&lt;h3 id="step-2-remove-the-vcf-automation-vcf-services-runtime"&gt;Step 2: Remove the VCF Automation VCF Services Runtime&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Deleting a VCF Services Runtime requires this script to be executed as the &lt;strong&gt;root&lt;/strong&gt; user inside the &lt;strong&gt;SDDC Manager&lt;/strong&gt; virtual machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="list-vcf-services-runtime"&gt;List VCF Services Runtime&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py list vsp-cluster --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="delete-vcf-services-runtime"&gt;Delete VCF Services Runtime&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python cleanup_component.py delete vsp-cluster --component-id &amp;lt;vsp-cluster-id&amp;gt; --fleet-fqdn &amp;lt;fleet-lcm-fqdn&amp;gt; --vcf-services-runtime-fqdn &amp;lt;vcf-services-runtime-fqdn&amp;gt; --vcf-services-runtime-username &amp;lt;vcf-services-runtime-username&amp;gt; --vcenter-username &amp;lt;vcenter-username&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;cleanup_component.py&lt;/code&gt; script is a handy workaround for the missing &amp;ldquo;Delete&amp;rdquo; option in VCF Operations 9.1. The key things to remember:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Back up&lt;/strong&gt; any critical component data before running delete operations.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;OVA components&lt;/strong&gt;, deletion also removes VMs from vCenter.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;VSP components&lt;/strong&gt;, deletion also removes the deployment from VCF Services Runtime.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;VCF Automation&lt;/strong&gt;, always delete VCF Automation and Migration Service Engine &lt;strong&gt;first&lt;/strong&gt;, then delete the VCF Services Runtime — and run that last step as &lt;strong&gt;root&lt;/strong&gt; on the SDDC Manager VM.&lt;/li&gt;
&lt;li&gt;Always verify the script checksum before running it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After removing components, also check Fleet password management for leftover or expired password accounts — I covered that remediation in &lt;a href="https://cosmin.us/fixing-expired-password-sync-in-vcf-9-1-fleet-management/"&gt;Fixing Expired Password Sync in VCF 9.1 Fleet Management&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the official script download and additional details, refer to &lt;a href="https://knowledge.broadcom.com/external/article/441333"&gt;Broadcom KB article 441333&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope this helps!&lt;/p&gt;</content:encoded></item><item><title>Installing and Configuring Cloudbase-Init on Windows for VCF Automation Guest Customization</title><link>https://cosmin.us/installing-and-configuring-cloudbase-init-on-windows-for-vcf-automation-guest-customization/</link><pubDate>Thu, 16 Apr 2026 15:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/installing-and-configuring-cloudbase-init-on-windows-for-vcf-automation-guest-customization/</guid><description>&lt;p&gt;Customizing Windows guest instances in VMware environments has traditionally been handled by the VMware Guest Customization Specification (Sysprep-based). While this approach works, it has limitations — especially when you need cloud-agnostic blueprints that work across vCenter, Azure, AWS, and GCP cloud accounts.&lt;/p&gt;
&lt;p&gt;Enter &lt;strong&gt;Cloudbase-Init&lt;/strong&gt; — the Windows equivalent of &lt;a href="https://cloud-init.io/"&gt;Cloud-Init&lt;/a&gt;. It provides powerful guest customization capabilities including user creation, password injection, hostname configuration, SSH public keys, and user-data script execution. This guide walks through the full process of installing and configuring Cloudbase-Init on a Windows Server template for use with &lt;strong&gt;VCF Automation&lt;/strong&gt; (formerly VMware Aria Automation / vRealize Automation).&lt;/p&gt;</description><content:encoded>&lt;p&gt;Customizing Windows guest instances in VMware environments has traditionally been handled by the VMware Guest Customization Specification (Sysprep-based). While this approach works, it has limitations — especially when you need cloud-agnostic blueprints that work across vCenter, Azure, AWS, and GCP cloud accounts.&lt;/p&gt;
&lt;p&gt;Enter &lt;strong&gt;Cloudbase-Init&lt;/strong&gt; — the Windows equivalent of &lt;a href="https://cloud-init.io/"&gt;Cloud-Init&lt;/a&gt;. It provides powerful guest customization capabilities including user creation, password injection, hostname configuration, SSH public keys, and user-data script execution. This guide walks through the full process of installing and configuring Cloudbase-Init on a Windows Server template for use with &lt;strong&gt;VCF Automation&lt;/strong&gt; (formerly VMware Aria Automation / vRealize Automation).&lt;/p&gt;
&lt;h2 id="what-is-cloudbase-init"&gt;What is Cloudbase-Init?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://cloudbase.it/cloudbase-init/"&gt;Cloudbase-Init&lt;/a&gt; is an open-source Windows guest initialization service, designed to be the Cloud-Init equivalent for Windows operating systems. Key features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User creation and password management&lt;/strong&gt; — Create local accounts, inject passwords, and enforce password change on first logon&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hostname customization&lt;/strong&gt; — Set the Windows computer name during provisioning&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSH public key injection&lt;/strong&gt; — Deploy SSH keys for remote access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-data scripts&lt;/strong&gt; — Execute PowerShell, batch, or cloud-config YAML scripts at first boot&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk extension&lt;/strong&gt; — Automatically extend volumes to use all available disk space&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MTU configuration&lt;/strong&gt; — Set network MTU values via DHCP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When paired with VCF Automation&amp;rsquo;s &lt;code&gt;cloudConfig&lt;/code&gt; and &lt;code&gt;remoteAccess&lt;/code&gt; blueprint properties, Cloudbase-Init enables fully automated Windows provisioning with zero manual intervention.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before you begin, ensure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Windows Server&lt;/strong&gt; ISO (2016, 2019, 2022, or 2025) mounted in vCenter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vCenter access&lt;/strong&gt; with permissions to create VMs and templates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VCF Automation&lt;/strong&gt; (or Aria Automation / vRealize Automation 8.x+) &lt;a href="https://cosmin.us/deploying-vmware-aria-automation-in-a-vcf-5-2-1-environment-step-by-step-guide/"&gt;deployed and configured&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;RDP client&lt;/strong&gt; for accessing the Windows VM during setup&lt;/li&gt;
&lt;li&gt;A vCenter &lt;strong&gt;Cloud Account&lt;/strong&gt; configured in VCF Automation (ideally using a &lt;a href="https://cosmin.us/creating-a-custom-role-in-vcenter-for-aria-automation-a-step-by-step-guide/"&gt;custom vCenter role&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="part-1-preparing-the-windows-template"&gt;Part 1: Preparing the Windows Template&lt;/h2&gt;
&lt;h3 id="step-1-create-a-base-windows-vm"&gt;Step 1: Create a Base Windows VM&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Log in to &lt;strong&gt;vCenter&lt;/strong&gt; and create a new Virtual Machine&lt;/li&gt;
&lt;li&gt;Mount your Windows Server ISO and complete the OS installation&lt;/li&gt;
&lt;li&gt;Install &lt;strong&gt;VMware Tools&lt;/strong&gt; (required for OVF metadata transport)&lt;/li&gt;
&lt;li&gt;Apply all &lt;strong&gt;Windows Updates&lt;/strong&gt; and install any additional software packages your organization requires (e.g., monitoring agents, antivirus)&lt;/li&gt;
&lt;li&gt;Power on the VM and log in via RDP&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Install all prerequisite software &lt;em&gt;before&lt;/em&gt; installing Cloudbase-Init. Once Cloudbase-Init runs Sysprep, the VM will shut down and should be converted to a template immediately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="step-2-download-cloudbase-init"&gt;Step 2: Download Cloudbase-Init&lt;/h3&gt;
&lt;p&gt;Download the Cloudbase-Init installation binaries from one of these locations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/cloudbase/cloudbase-init"&gt;https://github.com/cloudbase/cloudbase-init&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Official Downloads:&lt;/strong&gt; &lt;a href="https://cloudbase.it/cloudbase-init/#download"&gt;https://cloudbase.it/cloudbase-init/#download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Ensure you are using &lt;strong&gt;version 0.9.12.dev72 or greater&lt;/strong&gt;, which includes the &lt;a href="https://cloudbase-init.readthedocs.io/en/latest/services.html#ovf"&gt;OvfService&lt;/a&gt; metadata provider required for vCenter integration. The OvfService is what allows Cloudbase-Init to receive customization data from VCF Automation via VMware Tools.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-1.webp" alt="Cloudbase-Init GitHub download page showing stable and beta installer links" width="1590" height="1370" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="step-3-run-the-cloudbase-init-installer"&gt;Step 3: Run the Cloudbase-Init Installer&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Run the &lt;strong&gt;CloudbaseInitSetup_x64.msi&lt;/strong&gt; installer on the Windows VM&lt;/li&gt;
&lt;li&gt;Accept the license agreement and click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;On the configuration page, set the following options:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;Administrator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check:&lt;/strong&gt; ✅ &lt;em&gt;Run Cloudbase-Init service as LocalSystem&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Leave all other defaults as-is&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-2.webp" alt="Cloudbase-Init installer welcome screen" width="992" height="776" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-3.webp" alt="License agreement — accept and click Next" width="990" height="774" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-4.webp" alt="Custom setup — select features to install" width="990" height="774" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-5.webp" alt="Configuration options — set Username to Administrator and check Run as LocalSystem" width="990" height="774" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;, then click &lt;strong&gt;Install&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some advanced user-data scripts may require Cloudbase-Init to run under a dedicated administrator account rather than LocalSystem. If this applies to your environment, create and select a dedicated service account at install time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-6.webp" alt="Ready to install — click Install to proceed" width="990" height="774" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;&lt;strong&gt;Do NOT finalize the installer yet&lt;/strong&gt; — when prompted with the &amp;ldquo;Completing the Cloudbase-Init Setup Wizard&amp;rdquo; dialog, leave the checkboxes &lt;strong&gt;unchecked&lt;/strong&gt; for now. We need to edit the configuration files first.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="step-4-configure-cloudbase-init-unattendconf"&gt;Step 4: Configure cloudbase-init-unattend.conf&lt;/h3&gt;
&lt;p&gt;Navigate to the Cloudbase-Init installation directory (typically &lt;code&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\&lt;/code&gt;) and edit the file &lt;strong&gt;cloudbase-init-unattend.conf&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This configuration file controls the behavior during the &lt;strong&gt;specialize&lt;/strong&gt; phase of Windows setup (before the first user logon). Replace its contents with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[DEFAULT]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Administrator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Administrators&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;inject_user_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;config_drive_raw_hhd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;config_drive_cdrom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;config_drive_vfat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;bsdtar_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\bsdtar.exe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;mtools_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;logdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\log\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cloudbase-init-unattend.log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;default_log_levels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;logging_serial_port_settings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;mtu_use_dhcp_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ntp_use_dhcp_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;local_scripts_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\LocalScripts\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;metadata_services&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cloudbaseinit.metadata.services.ovfservice.OvfService&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cloudbaseinit.plugins.common.mtu.MTUPlugin,cloudbaseinit.plugins.common.sethostname.SetHostNamePlugin,cloudbaseinit.plugins.windows.extendvolumes.ExtendVolumesPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;allow_reboot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;stop_service_on_exit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;check_latest_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Key settings explained:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Setting&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;metadata_services&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;OvfService&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Only use the OVF metadata provider (vCenter). Prevents Cloudbase-Init from iterating through unsupported providers and cluttering logs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;plugins&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;MTU, SetHostName, ExtendVolumes&lt;/td&gt;
					&lt;td&gt;Specialize-phase plugins: configure network MTU, set hostname, extend disk volumes&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;allow_reboot&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Prevent unexpected reboots during the specialize phase&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;stop_service_on_exit&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Keep the service running for the next phase&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="step-5-configure-cloudbase-initconf"&gt;Step 5: Configure cloudbase-init.conf&lt;/h3&gt;
&lt;p&gt;In the same &lt;code&gt;conf&lt;/code&gt; directory, edit &lt;strong&gt;cloudbase-init.conf&lt;/strong&gt;. This file controls the behavior during the &lt;strong&gt;first logon&lt;/strong&gt; phase.&lt;/p&gt;
&lt;p&gt;Replace its contents with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[DEFAULT]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Administrator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Administrators&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;inject_user_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;first_logon_behaviour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;config_drive_raw_hhd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;config_drive_cdrom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;config_drive_vfat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;bsdtar_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\bsdtar.exe&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;mtools_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\bin\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;logdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\log\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cloudbase-init.log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;default_log_levels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;logging_serial_port_settings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;mtu_use_dhcp_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ntp_use_dhcp_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;local_scripts_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\LocalScripts\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;metadata_services&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cloudbaseinit.metadata.services.ovfservice.OvfService&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cloudbaseinit.plugins.windows.createuser.CreateUserPlugin,cloudbaseinit.plugins.windows.setuserpassword.SetUserPasswordPlugin,cloudbaseinit.plugins.common.sshpublickeys.SetUserSSHPublicKeysPlugin,cloudbaseinit.plugins.common.userdata.UserDataPlugin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Key settings explained:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Setting&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;first_logon_behaviour&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;always&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Forces the user to change the password after first logon&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;metadata_services&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;OvfService&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Same as unattend — only use OVF metadata for vCenter&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;plugins&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;CreateUser, SetUserPassword, SetUserSSHPublicKeys, UserData&lt;/td&gt;
					&lt;td&gt;First-logon plugins: create user account, set password, inject SSH keys, execute user-data scripts&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The &lt;code&gt;username=Administrator&lt;/code&gt; setting combined with &lt;code&gt;SetUserPasswordPlugin&lt;/code&gt; and the &lt;code&gt;remoteAccess&lt;/code&gt; password in the blueprint allows Cloudbase-Init to change the initial blank password created after Sysprep.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="step-6-understanding-the-plugin-order"&gt;Step 6: Understanding the Plugin Order&lt;/h3&gt;
&lt;p&gt;Cloudbase-Init executes plugins in the order they are listed in the configuration. Here is a breakdown of both phases:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Specialize Phase (&lt;code&gt;cloudbase-init-unattend.conf&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MTUPlugin&lt;/strong&gt; — Configures network MTU based on DHCP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SetHostNamePlugin&lt;/strong&gt; — Sets the Windows computer name&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExtendVolumesPlugin&lt;/strong&gt; — Extends disk volumes to use all available space&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;First Logon Phase (&lt;code&gt;cloudbase-init.conf&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CreateUserPlugin&lt;/strong&gt; — Creates the local user account&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SetUserPasswordPlugin&lt;/strong&gt; — Sets the user password from metadata&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SetUserSSHPublicKeysPlugin&lt;/strong&gt; — Injects SSH public keys&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UserDataPlugin&lt;/strong&gt; — Executes user-data scripts (PowerShell, batch, cloud-config)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Only list plugins with capabilities supported by OvfService. This keeps logs clean and avoids error messages from plugins that cannot find their expected metadata.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="step-7-complete-the-installation-with-sysprep"&gt;Step 7: Complete the Installation with Sysprep&lt;/h3&gt;
&lt;p&gt;Now return to the Cloudbase-Init installer &amp;ldquo;Completing&amp;rdquo; dialog and check both boxes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Run Sysprep to create a generalized image&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Shutdown when Sysprep terminates&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/cloudbase-init-install-7.webp" alt="Cloudbase-Init installer completion — check both Sysprep and Shutdown checkboxes" width="990" height="772" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Finish&lt;/strong&gt;. The system will run Windows &lt;a href="https://cloudbase-init.readthedocs.io/en/latest/tutorial.html#sysprepping"&gt;Sysprep&lt;/a&gt; to generalize the image and then automatically shut down the VM.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If Sysprep appears to hang or the VM does not shut down, try uninstalling Cloudbase-Init, re-downloading the installer, and repeating the process. This can occur with certain Windows Server 2016 builds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="step-8-convert-to-template"&gt;Step 8: Convert to Template&lt;/h3&gt;
&lt;p&gt;Once the VM has shut down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In &lt;strong&gt;vCenter&lt;/strong&gt;, right-click the VM&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Template&lt;/strong&gt; → &lt;strong&gt;Convert to Template&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your Windows template is now ready for use with VCF Automation.&lt;/p&gt;
&lt;h3 id="silent-installation-alternative"&gt;Silent Installation (Alternative)&lt;/h3&gt;
&lt;p&gt;For automated template builds (e.g., with Packer or other automation tools), Cloudbase-Init supports silent installation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Download Cloudbase-Init&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class="n"&gt;-Uri&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://cloudbase.it/downloads/CloudbaseInitSetup_x64.msi&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-OutFile&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\CloudbaseInitSetup_x64.msi&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Silent install&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;msiexec&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;CloudbaseInitSetup_x64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;msi&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;qn&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;cloudbase-init&lt;/span&gt;&lt;span class="n"&gt;-install&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;log&lt;/span&gt; &lt;span class="p"&gt;^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LOGGINGSERIALPORTNAME&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;COM1&lt;/span&gt; &lt;span class="p"&gt;^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Administrator&lt;/span&gt; &lt;span class="p"&gt;^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;INJECTMETADATAPASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TRUE&lt;/span&gt; &lt;span class="p"&gt;^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LOCALSYSTEMSERVICE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TRUE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Copy custom configuration files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Copy-Item&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cloudbase-init.conf&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\cloudbase-init.conf&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Copy-Item&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cloudbase-init-unattend.conf&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\cloudbase-init-unattend.conf&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Run Sysprep&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\Windows\System32\Sysprep\sysprep.exe&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;generalize&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;oobe&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;unattend&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf\Unattend.xml&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="part-2-deploying-and-customizing-windows-in-vcf-automation"&gt;Part 2: Deploying and Customizing Windows in VCF Automation&lt;/h2&gt;
&lt;p&gt;Now that the template is prepared, let&amp;rsquo;s create a cloud-agnostic blueprint in VCF Automation to deploy and customize Windows instances.&lt;/p&gt;
&lt;h3 id="step-1-verify-infrastructure-setup"&gt;Step 1: Verify Infrastructure Setup&lt;/h3&gt;
&lt;p&gt;Ensure the following are configured in VCF Automation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cloud Account&lt;/strong&gt; — A vCenter Cloud Account is connected&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-1.webp" alt="Cloud Accounts page showing the vCenter connection" width="1890" height="948" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;Cloud Zone&lt;/strong&gt; — A Cloud Zone is associated with the Cloud Account&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-2.webp" alt="Cloud Zones page showing the configured zone" width="1888" height="960" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;Project&lt;/strong&gt; — A Project is linked to the Cloud Zone with appropriate users/groups&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-3.webp" alt="Projects page showing the cloudbase-init project" width="1986" height="1042" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;strong&gt;Flavor Mapping&lt;/strong&gt; — At least one flavor mapping (e.g., &lt;code&gt;small&lt;/code&gt; = 1 vCPU, 2 GB RAM)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-4.webp" alt="Flavor Mapping configuration page" width="2624" height="754" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;&lt;strong&gt;Image Mapping&lt;/strong&gt; — An image mapping pointing to your Cloudbase-Init Windows template&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-5.webp" alt="Image Mapping — map your Cloudbase-Init Windows template" width="2548" height="884" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you don&amp;rsquo;t see your template in the Image Mapping dropdown, navigate to &lt;strong&gt;Cloud Accounts&lt;/strong&gt; and click &lt;strong&gt;Sync Images&lt;/strong&gt;. By default, image collection runs automatically every 24 hours.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start="6"&gt;
&lt;li&gt;&lt;strong&gt;Storage Profile&lt;/strong&gt; — A storage profile for your vCenter datastore(s)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-6.webp" alt="Storage Profiles page showing the default profile" width="2746" height="736" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="step-2-create-the-blueprint"&gt;Step 2: Create the Blueprint&lt;/h3&gt;
&lt;p&gt;Navigate to &lt;strong&gt;Design&lt;/strong&gt; → &lt;strong&gt;Cloud Templates&lt;/strong&gt; and create a new template for your project.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-7.webp" alt="New Blueprint dialog — name and assign to project" width="1160" height="1020" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Here is an example cloud-agnostic blueprint that deploys the Windows image with guest customization:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;formatVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Hostname&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;win-demo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Administrator Password&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;encrypted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;P@ssw0rd!2024&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Cloud_Machine_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Cloud.Machine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;win2022&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;flavor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;small&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;remoteAccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;authentication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;usernamePassword&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Administrator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${input.password}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cloudConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; #cloud-config
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; set_hostname: ${input.hostname}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; write_files:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - content: |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Cloudbase-Init deployment successful!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Deployed by VCF Automation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Hostname: ${input.hostname}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; path: C:\deployment-info.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-8.webp" alt="Blueprint editor showing the cloud-agnostic YAML with remoteAccess and cloudConfig" width="2440" height="1280" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If you also need extra data disks in your Windows blueprints, I covered &lt;a href="https://cosmin.us/how-to-add-custom-disks-with-drive-letters-and-labels-in-aria-automation-vmware-by-broadcom/"&gt;adding custom disks with drive letters and labels in Aria Automation&lt;/a&gt; separately.&lt;/p&gt;
&lt;h3 id="understanding-the-blueprint-properties"&gt;Understanding the Blueprint Properties&lt;/h3&gt;
&lt;h4 id="remoteaccess"&gt;remoteAccess&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;remoteAccess&lt;/code&gt; section provides credentials to the guest instance:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Property&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;authentication&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Authentication type: &lt;code&gt;usernamePassword&lt;/code&gt;, &lt;code&gt;generatedPublicPrivateKey&lt;/code&gt;, or &lt;code&gt;publicPrivateKey&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;username&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The local account name (matches &lt;code&gt;username&lt;/code&gt; in cloudbase-init.conf)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;password&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The password to set (must meet Windows password policy requirements)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The OVF metadata service picks up these values and exposes them to the &lt;strong&gt;CreateUserPlugin&lt;/strong&gt; and &lt;strong&gt;SetUserPasswordPlugin&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id="cloudconfig"&gt;cloudConfig&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;cloudConfig&lt;/code&gt; property sets the user-data payload. The metadata service picks it up and passes it to the &lt;strong&gt;UserDataPlugin&lt;/strong&gt;. The plugin interprets the data based on the first line:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;First Line&lt;/th&gt;
					&lt;th&gt;Script Type&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;#cloud-config&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Cloud-config YAML (hostname, write_files, etc.)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;#ps1&lt;/code&gt; or &lt;code&gt;#ps1_sysnative&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;PowerShell script&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;#cmd&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Windows batch/cmd script&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;rem cmd&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Windows batch/cmd script (alternative)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example: PowerShell user-data&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;cloudConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; #ps1_sysnative
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; # Install Chocolatey
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Set-ExecutionPolicy Bypass -Scope Process -Force
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; iex ((New-Object System.Net.WebClient).DownloadString(&amp;#39;https://community.chocolatey.org/install.ps1&amp;#39;))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; # Install common tools
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; choco install notepadplusplus 7zip git -y
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; # Create a marker file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Set-Content -Path &amp;#34;C:\deployment-complete.txt&amp;#34; -Value &amp;#34;Deployment completed at $(Get-Date)&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Example: Cloud-config with multiple write_files&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;cloudConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; #cloud-config
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; set_hostname: myserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; write_files:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - content: |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Server provisioned by VCF Automation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Date: 2026-04-16
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; path: C:\info.txt
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; - content: |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; # Custom PowerShell profile
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; Set-Alias ll Get-ChildItem
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; path: C:\Users\Administrator\Documents\WindowsPowerShell\profile.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-3-deploy-the-blueprint"&gt;Step 3: Deploy the Blueprint&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; (or &lt;strong&gt;Test&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Provide a &lt;strong&gt;Deployment Name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Fill in the input parameters (hostname, password)&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Current Draft&lt;/strong&gt; as the version&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;VCF Automation will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the Windows template&lt;/li&gt;
&lt;li&gt;Power on the VM&lt;/li&gt;
&lt;li&gt;VMware Tools will transport the OVF metadata to the guest&lt;/li&gt;
&lt;li&gt;Cloudbase-Init will pick up the metadata and execute the configured plugins&lt;/li&gt;
&lt;li&gt;The hostname will be set, user account configured, and user-data scripts executed&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="step-4-verify-the-customization"&gt;Step 4: Verify the Customization&lt;/h3&gt;
&lt;p&gt;Once the deployment completes successfully:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open an &lt;strong&gt;RDP client&lt;/strong&gt; and connect to the new VM using the credentials from the blueprint&lt;/li&gt;
&lt;li&gt;On first login, you will be &lt;strong&gt;prompted to change the password&lt;/strong&gt; (due to &lt;code&gt;first_logon_behaviour=always&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-10.webp" alt="Windows prompting the user to change their password before signing in" width="856" height="292" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;After changing the password, verify:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hostname&lt;/strong&gt; — Open a command prompt and run &lt;code&gt;hostname&lt;/code&gt; to confirm the custom name was applied&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment file&lt;/strong&gt; — Check &lt;code&gt;C:\deployment-info.txt&lt;/code&gt; (or your custom file path) for the content specified in &lt;code&gt;write_files&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User account&lt;/strong&gt; — Confirm the Administrator account is active with the correct group membership&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-11.webp" alt="Windows Explorer showing C:\ drive with the test.txt file created by Cloudbase-Init" width="1578" height="574" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/04/cloudbase-init/deployment-12.webp" alt="System Properties confirming the hostname was set to “demoname” by Cloudbase-Init" width="1638" height="812" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;
&lt;h3 id="cloudbase-init-logs"&gt;Cloudbase-Init Logs&lt;/h3&gt;
&lt;p&gt;If customization doesn&amp;rsquo;t work as expected, check the logs at:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\log\cloudbase-init.log
C:\Program Files\Cloudbase Solutions\Cloudbase-Init\log\cloudbase-init-unattend.log
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="common-issues"&gt;Common Issues&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Issue&lt;/th&gt;
					&lt;th&gt;Cause&lt;/th&gt;
					&lt;th&gt;Solution&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Hostname not set&lt;/td&gt;
					&lt;td&gt;SetHostNamePlugin not in unattend plugins list&lt;/td&gt;
					&lt;td&gt;Add &lt;code&gt;SetHostNamePlugin&lt;/code&gt; to &lt;code&gt;cloudbase-init-unattend.conf&lt;/code&gt; plugins&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Password not working&lt;/td&gt;
					&lt;td&gt;Password doesn&amp;rsquo;t meet Windows policy&lt;/td&gt;
					&lt;td&gt;Use a password with uppercase, lowercase, number, and special character&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;User-data script not executing&lt;/td&gt;
					&lt;td&gt;Wrong first-line header&lt;/td&gt;
					&lt;td&gt;Ensure the first line is &lt;code&gt;#ps1_sysnative&lt;/code&gt;, &lt;code&gt;#cloud-config&lt;/code&gt;, etc.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Metadata not found&lt;/td&gt;
					&lt;td&gt;VMware Tools not installed&lt;/td&gt;
					&lt;td&gt;Install VMware Tools on the template before Cloudbase-Init&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Sysprep hangs&lt;/td&gt;
					&lt;td&gt;Installer corruption&lt;/td&gt;
					&lt;td&gt;Re-download and reinstall Cloudbase-Init&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Logs show &amp;ldquo;iterating metadata services&amp;rdquo;&lt;/td&gt;
					&lt;td&gt;Multiple metadata_services listed&lt;/td&gt;
					&lt;td&gt;Set only &lt;code&gt;OvfService&lt;/code&gt; in the metadata_services line&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="checking-the-ovf-environment"&gt;Checking the OVF Environment&lt;/h3&gt;
&lt;p&gt;To verify that VMware Tools is properly transporting OVF metadata, you can check the OVF environment XML on the guest:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Check if OVF environment is available&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Get-Content&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\Program Files\VMware\VMware Tools\OvfEnvironment.xml&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-ErrorAction&lt;/span&gt; &lt;span class="n"&gt;SilentlyContinue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="advanced-configurations"&gt;Advanced Configurations&lt;/h2&gt;
&lt;h3 id="adding-custom-scripts-to-localscripts"&gt;Adding Custom Scripts to LocalScripts&lt;/h3&gt;
&lt;p&gt;You can place PowerShell or batch scripts in the LocalScripts directory that Cloudbase-Init will execute at boot:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;C:\Program Files\Cloudbase Solutions\Cloudbase-Init\LocalScripts\
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Scripts in this directory are executed in alphabetical order. This is useful for scripts that need to run on every boot rather than just the first boot.&lt;/p&gt;
&lt;h3 id="using-cloudbase-init-with-packer"&gt;Using Cloudbase-Init with Packer&lt;/h3&gt;
&lt;p&gt;For fully automated template creation with HashiCorp Packer, add a provisioner step:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;powershell&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; inline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Invoke-WebRequest -Uri &amp;#39;https://cloudbase.it/downloads/CloudbaseInitSetup_x64.msi&amp;#39; -OutFile &amp;#39;C:\\CloudbaseInitSetup_x64.msi&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; &amp;#34;Start-Process msiexec -ArgumentList &amp;#39;/i C:\\CloudbaseInitSetup_x64.msi /qn LOGGINGSERIALPORTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;COM1 USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Administrator INJECTMETADATAPASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TRUE LOCALSYSTEMSERVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="k"&gt;Wait&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;file&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;conf/cloudbase-init.conf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\\Program Files\\Cloudbase Solutions\\Cloudbase-Init\\conf\\cloudbase-init.conf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;file&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;conf/cloudbase-init-unattend.conf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\\Program Files\\Cloudbase Solutions\\Cloudbase-Init\\conf\\cloudbase-init-unattend.conf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="multi-cloud-blueprints"&gt;Multi-Cloud Blueprints&lt;/h3&gt;
&lt;p&gt;One of the biggest advantages of Cloudbase-Init is &lt;strong&gt;cloud-agnostic blueprints&lt;/strong&gt;. The same blueprint and &lt;code&gt;cloudConfig&lt;/code&gt; work across vCenter, Azure, AWS, and GCP cloud accounts — Cloudbase-Init automatically detects the appropriate metadata service for each cloud provider.&lt;/p&gt;
&lt;p&gt;For vCenter-only deployments, specifying &lt;code&gt;OvfService&lt;/code&gt; alone in the configuration is recommended for cleaner logs and faster initialization.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Cloudbase-Init is a powerful and flexible tool for Windows guest customization in VCF Automation environments. By following this guide, you have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ Installed Cloudbase-Init on a Windows Server VM&lt;/li&gt;
&lt;li&gt;✅ Configured both &lt;code&gt;cloudbase-init-unattend.conf&lt;/code&gt; (specialize phase) and &lt;code&gt;cloudbase-init.conf&lt;/code&gt; (first logon phase) for OvfService&lt;/li&gt;
&lt;li&gt;✅ Prepared a generalized Windows template via Sysprep&lt;/li&gt;
&lt;li&gt;✅ Created a cloud-agnostic blueprint with &lt;code&gt;remoteAccess&lt;/code&gt; and &lt;code&gt;cloudConfig&lt;/code&gt; properties&lt;/li&gt;
&lt;li&gt;✅ Deployed and verified the customized Windows instance&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach gives you consistent, repeatable Windows provisioning with the flexibility to customize user accounts, hostnames, file creation, and script execution — all without manual intervention.&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloudbase-init.readthedocs.io/en/latest/"&gt;Cloudbase-Init Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cloudbase/cloudbase-init"&gt;Cloudbase-Init GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudbase-init.readthedocs.io/en/latest/services.html#ovf"&gt;Cloudbase-Init OvfService Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudbase-init.readthedocs.io/en/latest/plugins.html"&gt;Cloudbase-Init Plugins Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudbase-init.readthedocs.io/en/latest/userdata.html"&gt;Cloudbase-Init UserData Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.archive.org/web/20230224190918/https://blogs.vmware.com/management/2019/11/cloudbase-init-windows-initialization.html"&gt;VMware Blog — Windows Guest Initialization with Cloudbase-Init (Archived)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@mpoore/installing-cloudbase-init-on-windows-for-vra-customisation-455cf0a80430"&gt;Michael Poore — Installing Cloudbase-Init on Windows for vRA Customisation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Querying Aria Operations for Logs API by Hostname – A Complete Guide</title><link>https://cosmin.us/querying-aria-operations-for-logs-api-by-hostname/</link><pubDate>Thu, 02 Apr 2026 14:20:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/querying-aria-operations-for-logs-api-by-hostname/</guid><description>&lt;p&gt;If you need to pull log events programmatically from VMware Aria Operations for Logs (formerly vRealize Log Insight), filtering by a specific hostname, this guide walks you through the entire process — from authentication to querying the powerful internal API endpoint.&lt;/p&gt;
&lt;p&gt;This approach is inspired by &lt;a href="https://www.brockpeterson.com/post/pull-events-from-the-aria-operations-for-logs-api"&gt;Brock Peterson&amp;rsquo;s excellent series on the Aria Operations for Logs API&lt;/a&gt;, expanded here with full session authentication steps and hostname-specific query examples.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before we begin, you&amp;rsquo;ll need:&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you need to pull log events programmatically from VMware Aria Operations for Logs (formerly vRealize Log Insight), filtering by a specific hostname, this guide walks you through the entire process — from authentication to querying the powerful internal API endpoint.&lt;/p&gt;
&lt;p&gt;This approach is inspired by &lt;a href="https://www.brockpeterson.com/post/pull-events-from-the-aria-operations-for-logs-api"&gt;Brock Peterson&amp;rsquo;s excellent series on the Aria Operations for Logs API&lt;/a&gt;, expanded here with full session authentication steps and hostname-specific query examples.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before we begin, you&amp;rsquo;ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Aria Operations for Logs&lt;/strong&gt; &lt;a href="https://cosmin.us/deploying-vmware-aria-operations-for-logs-in-a-vcf-5-2-1-environment-step-by-step-guide/"&gt;deployed&lt;/a&gt; and accessible (FQDN or IP)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;local admin account&lt;/strong&gt; or Active Directory / vIDM credentials&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;curl&lt;/strong&gt; installed on your workstation&lt;/li&gt;
&lt;li&gt;Access to &lt;strong&gt;port 9543&lt;/strong&gt; on the Aria Operations for Logs appliance&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; All examples use the &lt;code&gt;-k&lt;/code&gt; flag to skip SSL certificate verification. In production, you should use proper certificate trust chains instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-1-authenticate-and-retrieve-a-session-token"&gt;Step 1: Authenticate and Retrieve a Session Token&lt;/h2&gt;
&lt;p&gt;Every API interaction starts with obtaining a Bearer token. You do this by posting your credentials to the &lt;code&gt;/api/v2/sessions&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;h3 id="local-authentication"&gt;Local Authentication&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN:9543/api/v2/sessions&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;username&amp;#34;: &amp;#34;admin&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;password&amp;#34;: &amp;#34;YourPasswordHere&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;provider&amp;#34;: &amp;#34;Local&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="active-directory-authentication"&gt;Active Directory Authentication&lt;/h3&gt;
&lt;p&gt;If your environment uses Active Directory, change the &lt;code&gt;provider&lt;/code&gt; value:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN:9543/api/v2/sessions&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;username&amp;#34;: &amp;#34;your_ad_user&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;password&amp;#34;: &amp;#34;YourPasswordHere&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;provider&amp;#34;: &amp;#34;ActiveDirectory&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="vidm-workspace-one-access-authentication"&gt;vIDM (Workspace ONE Access) Authentication&lt;/h3&gt;
&lt;p&gt;For federated identity via vIDM:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN:9543/api/v2/sessions&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;username&amp;#34;: &amp;#34;user@domain.com&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;password&amp;#34;: &amp;#34;YourPasswordHere&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;provider&amp;#34;: &amp;#34;vIDM&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="understanding-the-response"&gt;Understanding the Response&lt;/h3&gt;
&lt;p&gt;All three methods return the same JSON structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;userId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;3c1b81cc-418e-44c0-b91a-54e10a87b1d3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sessionId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1a2b3c4d-your-session-token-here==&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ttl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;userId&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Unique identifier for the authenticated user&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;sessionId&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;This is your Bearer token&lt;/strong&gt; — save it for all subsequent API calls&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ttl&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Token time-to-live in seconds (default: 1800 = 30 minutes)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Copy the &lt;code&gt;sessionId&lt;/code&gt; value — this is the Bearer token you&amp;rsquo;ll use in every subsequent request.&lt;/p&gt;
&lt;h2 id="step-2-verify-your-session-optional"&gt;Step 2: Verify Your Session (Optional)&lt;/h2&gt;
&lt;p&gt;Before running queries, you can verify your token is valid:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN:9543/api/v2/sessions/current&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A successful response confirms your session is active and returns your user details.&lt;/p&gt;
&lt;h2 id="step-3-discover-the-internal-api"&gt;Step 3: Discover the Internal API&lt;/h2&gt;
&lt;p&gt;The standard &lt;code&gt;/api/v2/events&lt;/code&gt; endpoint works but is unintuitive and poorly documented. The &lt;strong&gt;internal API&lt;/strong&gt; is far more powerful and flexible.&lt;/p&gt;
&lt;p&gt;You can browse the internal API documentation at:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;https://YOUR_LOG_INSIGHT_FQDN/rest-api/internal
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Scroll down to the &lt;strong&gt;events&lt;/strong&gt; section and you&amp;rsquo;ll find &lt;code&gt;POST /events/query&lt;/code&gt; — this is the endpoint we&amp;rsquo;ll use for all our hostname queries.&lt;/p&gt;
&lt;h2 id="step-4-query-events-by-hostname"&gt;Step 4: Query Events by Hostname&lt;/h2&gt;
&lt;p&gt;Now for the main event. The internal &lt;code&gt;POST /events/query&lt;/code&gt; endpoint accepts a JSON body with a &lt;code&gt;constraints&lt;/code&gt; field that uses the same query language as the Explore Logs UI.&lt;/p&gt;
&lt;h3 id="basic-hostname-query-last-5-minutes"&gt;Basic Hostname Query (Last 5 Minutes)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;esxi01.lab.local\&amp;#34; &amp;amp; LastFiveMinutes&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="hostname-query-last-24-hours"&gt;Hostname Query (Last 24 Hours)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;esxi01.lab.local\&amp;#34; &amp;amp; LastDay&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="hostname--text-search"&gt;Hostname + Text Search&lt;/h3&gt;
&lt;p&gt;Search for the word &amp;ldquo;error&amp;rdquo; on a specific host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;esxi01.lab.local\&amp;#34; &amp;amp; &amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;error&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;amp; LastDay&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="multiple-hostnames-with-or"&gt;Multiple Hostnames with OR&lt;/h3&gt;
&lt;p&gt;Query logs from two different hosts simultaneously:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;(hostname:\&amp;#34;esxi01.lab.local\&amp;#34; | hostname:\&amp;#34;esxi02.lab.local\&amp;#34;) &amp;amp; LastDay&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="hostname--specific-field-exists"&gt;Hostname + Specific Field Exists&lt;/h3&gt;
&lt;p&gt;Find vCenter events logged from a specific host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;vcsa01.lab.local\&amp;#34; &amp;amp; EXISTS(vc_event_type) &amp;amp; LastDay&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="hostname--specific-field-value"&gt;Hostname + Specific Field Value&lt;/h3&gt;
&lt;p&gt;Filter a host for specific vCenter event types (e.g., user login events):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;vcsa01.lab.local\&amp;#34; &amp;amp; vc_event_type:\&amp;#34;com.vmware.vim25.userloginsessionevent\&amp;#34; &amp;amp; Last48&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 20000,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="hostname--epoch-timestamp-range"&gt;Hostname + Epoch Timestamp Range&lt;/h3&gt;
&lt;p&gt;For precise time ranges, use seconds-since-epoch timestamps:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;esxi01.lab.local\&amp;#34; &amp;amp; (timestamp &amp;gt; 1743552000 &amp;amp; timestamp &amp;lt; 1743638400)&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use a tool like &lt;a href="https://www.epochconverter.com/"&gt;epochconverter.com&lt;/a&gt; to convert human-readable dates to epoch timestamps.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="hostname-with-aggregation-event-count-by-type"&gt;Hostname with Aggregation (Event Count by Type)&lt;/h3&gt;
&lt;p&gt;Get a count of events grouped by type for a specific host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -k -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer YOUR_SESSION_ID_HERE&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;esxi01.lab.local\&amp;#34; &amp;amp; LastDay&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;COUNT(event)&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;event_type&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;event ASC&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 20000,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="quick-reference-constraint-syntax"&gt;Quick Reference: Constraint Syntax&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Syntax&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;hostname:\&amp;quot;value\&amp;quot;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Filter by exact hostname&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;'text string'&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Search for text in log messages&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;AND operator&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;|&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;OR operator&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;EXISTS(field)&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Check if a field exists in the log entry&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;field:\&amp;quot;value\&amp;quot;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Filter by field value&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LastFiveMinutes&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Last 5 minutes of logs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LastDay&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Last 24 hours&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Last48&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Last 48 hours&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;timestamp &amp;gt; epoch&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Custom epoch-based time range&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="quick-reference-request-body-fields"&gt;Quick Reference: Request Body Fields&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Field&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
					&lt;th&gt;Notes&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;constraints&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Your query (filters, time range)&lt;/td&gt;
					&lt;td&gt;Required&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;aggregations&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Aggregate function (e.g., &lt;code&gt;COUNT(event)&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;Leave empty for raw logs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;groups&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Group-by field&lt;/td&gt;
					&lt;td&gt;Leave empty for raw logs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;order&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Sort order for aggregations&lt;/td&gt;
					&lt;td&gt;Leave empty for raw logs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;resultsFrom&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Starting result index&lt;/td&gt;
					&lt;td&gt;Starts at 1&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;resultsTo&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Ending result index&lt;/td&gt;
					&lt;td&gt;Maximum is 20,000&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Query timeout in milliseconds&lt;/td&gt;
					&lt;td&gt;30000 = 30 seconds&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="putting-it-all-together-a-complete-script"&gt;Putting It All Together: A Complete Script&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a one-liner that authenticates and queries in a single pipeline:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Authenticate and store the token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -sk -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN:9543/api/v2/sessions&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{&amp;#34;username&amp;#34;:&amp;#34;admin&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;YourPasswordHere&amp;#34;,&amp;#34;provider&amp;#34;:&amp;#34;Local&amp;#34;}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; python3 -c &lt;span class="s2"&gt;&amp;#34;import sys,json; print(json.load(sys.stdin)[&amp;#39;sessionId&amp;#39;])&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Query events for a specific hostname&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sk -X POST &lt;span class="s2"&gt;&amp;#34;https://YOUR_LOG_INSIGHT_FQDN/api/v1/events/query&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;constraints&amp;#34;: &amp;#34;hostname:\&amp;#34;esxi01.lab.local\&amp;#34; &amp;amp; LastDay&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;aggregations&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;groups&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;order&amp;#34;: &amp;#34;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsFrom&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;resultsTo&amp;#34;: 500,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;timeout&amp;#34;: 30000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; python3 -m json.tool
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="important-notes"&gt;Important Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Field name verification:&lt;/strong&gt; The &lt;code&gt;hostname&lt;/code&gt; field is the standard syslog source field in Aria Operations for Logs. However, depending on your ingestion method and content packs, the field could be named &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;vmw_esxi_hostname&lt;/code&gt;, or something custom. Check the field list in the &lt;strong&gt;Explore Logs&lt;/strong&gt; UI sidebar to confirm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token expiration:&lt;/strong&gt; The default TTL is 30 minutes (1800 seconds). For longer-running scripts, re-authenticate periodically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Result limits:&lt;/strong&gt; The maximum value for &lt;code&gt;resultsTo&lt;/code&gt; is 20,000. For larger result sets, use the UI export-to-NFS feature as described in &lt;a href="https://www.brockpeterson.com/post/aria-operations-for-logs-export-events-and-charts"&gt;Brock Peterson&amp;rsquo;s export guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal API caveat:&lt;/strong&gt; The &lt;code&gt;POST /events/query&lt;/code&gt; endpoint is part of the &lt;strong&gt;internal&lt;/strong&gt; API surface. While it&amp;rsquo;s far more powerful than the public &lt;code&gt;/api/v2/events&lt;/code&gt; endpoint, internal APIs may change between versions without notice.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Custom Rightsizing Guardrails in VCF Operations 9 with Super Metrics</title><link>https://cosmin.us/custom-rightsizing-guardrails-in-vcf-operations-9-with-super-metrics/</link><pubDate>Tue, 31 Mar 2026 23:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/custom-rightsizing-guardrails-in-vcf-operations-9-with-super-metrics/</guid><description>&lt;p&gt;In a &lt;a href="https://cosmin.us/using-90-days-of-usage-for-rightsizing-recommendations-in-vcf-operations-9/"&gt;previous post&lt;/a&gt; I covered how to configure VCF Operations 9 to use 90 days of usage data for rightsizing recommendations. That gives you a solid foundation — but once you start reviewing the recommendations, you will quickly notice that the engine does not enforce any organizational sizing standards.&lt;/p&gt;
&lt;p&gt;For example, the engine might recommend reducing a VM from 8 vCPUs to 5, or suggest removing just 1 GB of RAM. Both are technically valid from a demand perspective, but in practice:&lt;/p&gt;</description><content:encoded>&lt;p&gt;In a &lt;a href="https://cosmin.us/using-90-days-of-usage-for-rightsizing-recommendations-in-vcf-operations-9/"&gt;previous post&lt;/a&gt; I covered how to configure VCF Operations 9 to use 90 days of usage data for rightsizing recommendations. That gives you a solid foundation — but once you start reviewing the recommendations, you will quickly notice that the engine does not enforce any organizational sizing standards.&lt;/p&gt;
&lt;p&gt;For example, the engine might recommend reducing a VM from 8 vCPUs to 5, or suggest removing just 1 GB of RAM. Both are technically valid from a demand perspective, but in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Odd vCPU counts&lt;/strong&gt; can cause NUMA alignment issues, conflict with per-socket licensing models, and break internal VM sizing templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sub-2GB memory changes&lt;/strong&gt; create noise in your rightsizing reports without delivering meaningful capacity savings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;VCF Operations does not have native policy settings for these guardrails. But you can build them using &lt;strong&gt;Super Metrics&lt;/strong&gt; — custom formulas that calculate on every collection cycle and produce metrics you can use in dashboards, views, and reports.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before going further, I want to credit &lt;a href="https://github.com/brockpeterson/operations_supermetrics"&gt;Brock Peterson&amp;rsquo;s operations_supermetrics repository&lt;/a&gt; on GitHub. It contains dozens of production-ready Super Metrics in the correct VCF Operations JSON import format. The rightsizing Super Metrics in this post build on his work — specifically his &lt;a href="https://github.com/brockpeterson/operations_supermetrics/blob/main/vcpu%20recommended%20(oversized%20vms).json"&gt;vCPU Recommended (oversized VMs)&lt;/a&gt; and &lt;a href="https://github.com/brockpeterson/operations_supermetrics/blob/main/memory%20recommended%20(oversized%20vms).json"&gt;Memory Recommended (oversized VMs)&lt;/a&gt; metrics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="understanding-the-super-metric-format"&gt;Understanding the Super Metric Format&lt;/h2&gt;
&lt;p&gt;Before building anything, it helps to understand the structure. When you export a Super Metric from VCF Operations, the JSON looks like this (taken from Brock Peterson&amp;rsquo;s repo):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;b3ac88b8-09b6-4fc3-a4b2-35e615f340c9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKinds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VirtualMachine&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;adapterKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VMWARE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modificationTime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1694040789186&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vCPU Recommended (oversized VMs)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;formula&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${this, metric=cpu|corecount_provisioned} - ${this, metric=summary|oversized|vcpus}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vCPU Recommended (oversized VMs)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;unitId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modifiedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;287ed085-c6ca-4255-b69c-956f1665ed51&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Key fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;outer key&lt;/strong&gt; is a UUID — VCF Operations generates this on import, so you can use any valid UUID.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;resourceKinds&lt;/strong&gt; — defines which object type the metric applies to (here: &lt;code&gt;VirtualMachine&lt;/code&gt; from the &lt;code&gt;VMWARE&lt;/code&gt; adapter).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;formula&lt;/strong&gt; — the expression. Uses &lt;code&gt;${this, metric=...}&lt;/code&gt; to reference metrics on the same object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;unitId&lt;/strong&gt; — the unit of measurement (&lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; for counts, &lt;code&gt;&amp;quot;kb&amp;quot;&lt;/code&gt; for kilobytes, &lt;code&gt;&amp;quot;gb&amp;quot;&lt;/code&gt; for gigabytes).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key metric paths for rightsizing are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cpu|corecount_provisioned&lt;/code&gt; — current allocated vCPUs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;summary|oversized|vcpus&lt;/code&gt; — recommended vCPU reduction count&lt;/li&gt;
&lt;li&gt;&lt;code&gt;summary|undersized|vcpus&lt;/code&gt; — recommended vCPU addition count&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config|hardware|memoryKB&lt;/code&gt; — current allocated memory in KB&lt;/li&gt;
&lt;li&gt;&lt;code&gt;summary|oversized|memory&lt;/code&gt; — recommended memory reduction in KB&lt;/li&gt;
&lt;li&gt;&lt;code&gt;summary|undersized|memory&lt;/code&gt; — recommended memory addition in KB&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="super-metric-1-even-vcpu-recommendations-oversized-vms"&gt;Super Metric 1: Even vCPU Recommendations (Oversized VMs)&lt;/h2&gt;
&lt;h3 id="the-problem"&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Brock&amp;rsquo;s original formula &lt;code&gt;${this, metric=cpu|corecount_provisioned} - ${this, metric=summary|oversized|vcpus}&lt;/code&gt; gives you the target vCPU count, but it can be odd. If a VM has 8 vCPUs and the engine recommends reducing by 3, the result is 5 — which does not align with standard sizing templates.&lt;/p&gt;
&lt;h3 id="the-formula"&gt;The Formula&lt;/h3&gt;
&lt;p&gt;To force even numbers, we divide by 2, truncate to an integer, and multiply back:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;(${this, metric=cpu|corecount_provisioned} - ${this, metric=summary|oversized|vcpus}) / 2 * 2
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; VCF Operations Super Metric formulas perform integer truncation when dividing integers. If the raw target is 5, then &lt;code&gt;5 / 2 = 2&lt;/code&gt; (truncated), and &lt;code&gt;2 * 2 = 4&lt;/code&gt;. If the raw target is 6, then &lt;code&gt;6 / 2 = 3&lt;/code&gt;, and &lt;code&gt;3 * 2 = 6&lt;/code&gt;. The result is always even, rounded down.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="downloadable-json"&gt;Downloadable JSON&lt;/h3&gt;
&lt;p&gt;📥 &lt;strong&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Recommended%20vCPUs%20Even%20(Oversized%20VMs).json"&gt;Download: Recommended vCPUs Even (Oversized VMs).json&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;a1c2d3e4-5f6a-7b8c-9d0e-1f2a3b4c5d6e&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKinds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VirtualMachine&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;adapterKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VMWARE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modificationTime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1711929600000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vCPU Recommended Even (Oversized VMs)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;formula&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;(${this, metric=cpu|corecount_provisioned} - ${this, metric=summary|oversized|vcpus}) / 2 * 2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Recommended vCPU count for oversized VMs, rounded down to the nearest even number. Based on Brock Peterson&amp;#39;s original vCPU Recommended super metric.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;unitId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modifiedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="super-metric-2-even-vcpu-recommendations-undersized-vms"&gt;Super Metric 2: Even vCPU Recommendations (Undersized VMs)&lt;/h2&gt;
&lt;p&gt;The same logic applies to undersized VMs, but we add the recommended increase instead of subtracting:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;(${this, metric=cpu|corecount_provisioned} + ${this, metric=summary|undersized|vcpus}) / 2 * 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If a VM has 2 vCPUs and the engine recommends adding 3, the raw target is 5 → &lt;code&gt;5 / 2 * 2 = 4&lt;/code&gt;. Rounded down to the next even number.&lt;/p&gt;
&lt;h3 id="downloadable-json-1"&gt;Downloadable JSON&lt;/h3&gt;
&lt;p&gt;📥 &lt;strong&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Recommended%20vCPUs%20Even%20(Undersized%20VMs).json"&gt;Download: Recommended vCPUs Even (Undersized VMs).json&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;b2d3e4f5-6a7b-8c9d-0e1f-2a3b4c5d6e7f&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKinds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VirtualMachine&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;adapterKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VMWARE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modificationTime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1711929600000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vCPU Recommended Even (Undersized VMs)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;formula&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;(${this, metric=cpu|corecount_provisioned} + ${this, metric=summary|undersized|vcpus}) / 2 * 2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Recommended vCPU count for undersized VMs, rounded up to the nearest even number. Based on Brock Peterson&amp;#39;s original vCPU Recommended super metric.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;unitId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modifiedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="super-metric-3-memory-reduction--2-gb-minimum-oversized-vms"&gt;Super Metric 3: Memory Reduction — 2 GB Minimum (Oversized VMs)&lt;/h2&gt;
&lt;h3 id="the-problem-1"&gt;The Problem&lt;/h3&gt;
&lt;p&gt;The engine might recommend removing 512 MB or 1 GB of RAM from a VM. On a 16 GB VM, that is not worth the operational overhead of a change window. You want to filter the noise and only see reductions of 2 GB or more.&lt;/p&gt;
&lt;h3 id="the-formula-1"&gt;The Formula&lt;/h3&gt;
&lt;p&gt;VCF Operations Super Metrics support the &lt;strong&gt;ternary operator&lt;/strong&gt; (&lt;code&gt;condition ? true_value : false_value&lt;/code&gt;) for conditional logic. The &lt;code&gt;summary|oversized|memory&lt;/code&gt; metric is in KB, so 2 GB = 2,097,152 KB:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;${this, metric=summary|oversized|memory} &amp;gt;= 2097152 ? ${this, metric=config|hardware|memoryKB} - ${this, metric=summary|oversized|memory} : 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This returns the recommended new memory value (in KB) only if the reduction is 2 GB or more. Otherwise it returns 0.&lt;/p&gt;
&lt;h3 id="downloadable-json-2"&gt;Downloadable JSON&lt;/h3&gt;
&lt;p&gt;📥 &lt;strong&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Memory%20Recommended%202GB%20Min%20(Oversized%20VMs).json"&gt;Download: Memory Recommended 2GB Min (Oversized VMs).json&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;c3e4f5a6-7b8c-9d0e-1f2a-3b4c5d6e7f8a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKinds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VirtualMachine&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;adapterKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VMWARE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modificationTime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1711929600000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Memory Recommended 2GB Min (Oversized VMs)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;formula&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${this, metric=summary|oversized|memory} &amp;gt;= 2097152 ? ${this, metric=config|hardware|memoryKB} - ${this, metric=summary|oversized|memory} : 0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Recommended memory for oversized VMs. Only returns a value when the recommended reduction is 2 GB (2097152 KB) or more. Returns 0 for reductions below 2 GB to reduce noise. Based on Brock Peterson&amp;#39;s original Memory Recommended super metric.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;unitId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;kb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modifiedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="super-metric-4-memory-addition--2-gb-minimum-undersized-vms"&gt;Super Metric 4: Memory Addition — 2 GB Minimum (Undersized VMs)&lt;/h2&gt;
&lt;p&gt;Same logic for undersized VMs — only surface memory additions of 2 GB or more:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;${this, metric=summary|undersized|memory} &amp;gt;= 2097152 ? ${this, metric=config|hardware|memoryKB} + ${this, metric=summary|undersized|memory} : 0
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="downloadable-json-3"&gt;Downloadable JSON&lt;/h3&gt;
&lt;p&gt;📥 &lt;strong&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Memory%20Recommended%202GB%20Min%20(Undersized%20VMs).json"&gt;Download: Memory Recommended 2GB Min (Undersized VMs).json&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;d4f5a6b7-8c9d-0e1f-2a3b-4c5d6e7f8a9b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKinds&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;resourceKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VirtualMachine&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;adapterKindKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VMWARE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modificationTime&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1711929600000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Memory Recommended 2GB Min (Undersized VMs)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;formula&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${this, metric=summary|undersized|memory} &amp;gt;= 2097152 ? ${this, metric=config|hardware|memoryKB} + ${this, metric=summary|undersized|memory} : 0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Recommended memory for undersized VMs. Only returns a value when the recommended increase is 2 GB (2097152 KB) or more. Returns 0 for increases below 2 GB to reduce noise. Based on Brock Peterson&amp;#39;s original Memory Recommended super metric.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;unitId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;kb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modifiedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="how-to-import"&gt;How to Import&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Download the JSON files above.&lt;/li&gt;
&lt;li&gt;In VCF Operations, navigate to &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Super Metrics&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;⋯&lt;/strong&gt; (ellipsis) menu and select &lt;strong&gt;Import&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Upload the JSON file. The Super Metric will appear in your list.&lt;/li&gt;
&lt;li&gt;Repeat for each file.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;After importing, the Super Metrics will NOT start calculating until you activate them in a policy. See the next section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="activating-in-your-policy"&gt;Activating in Your Policy&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policies&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policy Definition&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select your production policy.&lt;/li&gt;
&lt;li&gt;Scroll to the &lt;strong&gt;Super Metrics&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Find the imported Super Metrics and toggle them to &lt;strong&gt;Enabled&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Save the policy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After the next collection cycle (typically 5 minutes), the Super Metrics will begin calculating for all VMs governed by that policy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: If you have different policies for production vs. dev/test, you may only want these guardrails on the production policy. Dev/test environments might intentionally allow odd vCPU counts or smaller memory increments.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="using-the-custom-metrics"&gt;Using the Custom Metrics&lt;/h2&gt;
&lt;p&gt;Once activated, these Super Metrics appear as regular metrics on VM objects. You can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add them as columns in a &lt;strong&gt;custom View&lt;/strong&gt; or &lt;strong&gt;Dashboard&lt;/strong&gt; alongside the native rightsizing metrics.&lt;/li&gt;
&lt;li&gt;Include them in &lt;strong&gt;scheduled Reports&lt;/strong&gt; for quarterly capacity reviews.&lt;/li&gt;
&lt;li&gt;Compare the native recommended value against your guardrail-adjusted value to quantify the difference.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A useful dashboard layout:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Column&lt;/th&gt;
					&lt;th&gt;Source&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;VM Name&lt;/td&gt;
					&lt;td&gt;Built-in&lt;/td&gt;
					&lt;td&gt;Identifies the VM&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Current vCPUs&lt;/td&gt;
					&lt;td&gt;`cpu&lt;/td&gt;
					&lt;td&gt;corecount_provisioned`&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Native Recommended vCPUs&lt;/td&gt;
					&lt;td&gt;Brock&amp;rsquo;s original super metric&lt;/td&gt;
					&lt;td&gt;What the engine says&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Even Recommended vCPUs&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Your imported super metric&lt;/td&gt;
					&lt;td&gt;Guardrail-adjusted target&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Current Memory (KB)&lt;/td&gt;
					&lt;td&gt;`config&lt;/td&gt;
					&lt;td&gt;hardware&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Memory Recommended (2GB+ only)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Your imported super metric&lt;/td&gt;
					&lt;td&gt;Only meaningful changes&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="adjusting-the-thresholds"&gt;Adjusting the Thresholds&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Guardrail&lt;/th&gt;
					&lt;th&gt;Current Value&lt;/th&gt;
					&lt;th&gt;To Adjust&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;vCPU rounding&lt;/td&gt;
					&lt;td&gt;Nearest even (÷2 ×2)&lt;/td&gt;
					&lt;td&gt;Change the divisor to 4 for multiples of 4 (÷4 ×4)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Memory threshold&lt;/td&gt;
					&lt;td&gt;2 GB (2,097,152 KB)&lt;/td&gt;
					&lt;td&gt;Change &lt;code&gt;2097152&lt;/code&gt; to &lt;code&gt;4194304&lt;/code&gt; for a 4 GB minimum&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Edit the formula directly in the Super Metric editor and click &lt;strong&gt;Validate&lt;/strong&gt; to confirm.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="downloads"&gt;Downloads&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;File&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Recommended%20vCPUs%20Even%20(Oversized%20VMs).json"&gt;Recommended vCPUs Even (Oversized VMs).json&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Even vCPU target for oversized VMs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Recommended%20vCPUs%20Even%20(Undersized%20VMs).json"&gt;Recommended vCPUs Even (Undersized VMs).json&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Even vCPU target for undersized VMs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Memory%20Recommended%202GB%20Min%20(Oversized%20VMs).json"&gt;Memory Recommended 2GB Min (Oversized VMs).json&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Memory target with 2 GB minimum reduction&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a href="https://cosmin.us/downloads/super-metrics/Memory%20Recommended%202GB%20Min%20(Undersized%20VMs).json"&gt;Memory Recommended 2GB Min (Undersized VMs).json&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;Memory target with 2 GB minimum addition&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Import all four into &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Super Metrics&lt;/strong&gt; &amp;gt; &lt;strong&gt;⋯&lt;/strong&gt; &amp;gt; &lt;strong&gt;Import&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="references-vcf-9"&gt;References (VCF 9)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/brockpeterson/operations_supermetrics"&gt;Brock Peterson — operations_supermetrics (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.brockpeterson.com/post/vm-rightsizing-minimums-in-vcf-operations"&gt;Brock Peterson — VM Rightsizing Minimums in VCF Operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.brockpeterson.com/post/vcf-operations-custom-groups-custom-properties-and-super-metrics"&gt;Brock Peterson — VCF Operations Custom Groups, Custom Properties, and Super Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.vmware.com/us/en/vmware-cis/aria/aria-operations/9-0/vmware-aria-operations-configuration-guide-9-0/configuring-policies.html"&gt;Broadcom TechDocs — Configuring Policies (VCF Operations 9.0)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Importing a vCenter 8.x as a Workload Domain into VCF 9</title><link>https://cosmin.us/importing-a-vcenter-8x-as-a-workload-domain-into-vcf-9/</link><pubDate>Tue, 31 Mar 2026 23:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/importing-a-vcenter-8x-as-a-workload-domain-into-vcf-9/</guid><description>&lt;p&gt;As organizations &lt;a href="https://cosmin.us/upgrading-vcf-5-2-1-to-vcf-9-step-by-step/"&gt;adopt VMware Cloud Foundation 9.0&lt;/a&gt;, a common question comes up: &lt;strong&gt;what do I do with my existing vCenter 8.x environments?&lt;/strong&gt; The good news is that VCF 9.0 supports backwards compatibility with 8.x vCenter instances. You can import them as workload domains directly from the &lt;a href="https://cosmin.us/deploying-vcf-operations-9-from-ova/"&gt;VCF Operations console&lt;/a&gt;, bringing your legacy infrastructure under VCF management without rebuilding from scratch.&lt;/p&gt;
&lt;p&gt;In this post I will walk through the full import process — from prerequisites and networking requirements to NSX deployment and validation — so you know exactly what to expect before you hit the &amp;ldquo;Import&amp;rdquo; button.&lt;/p&gt;</description><content:encoded>&lt;p&gt;As organizations &lt;a href="https://cosmin.us/upgrading-vcf-5-2-1-to-vcf-9-step-by-step/"&gt;adopt VMware Cloud Foundation 9.0&lt;/a&gt;, a common question comes up: &lt;strong&gt;what do I do with my existing vCenter 8.x environments?&lt;/strong&gt; The good news is that VCF 9.0 supports backwards compatibility with 8.x vCenter instances. You can import them as workload domains directly from the &lt;a href="https://cosmin.us/deploying-vcf-operations-9-from-ova/"&gt;VCF Operations console&lt;/a&gt;, bringing your legacy infrastructure under VCF management without rebuilding from scratch.&lt;/p&gt;
&lt;p&gt;In this post I will walk through the full import process — from prerequisites and networking requirements to NSX deployment and validation — so you know exactly what to expect before you hit the &amp;ldquo;Import&amp;rdquo; button.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note on terminology: When the action is performed within the VCF Operations console, it is referred to as an &lt;strong&gt;Import&lt;/strong&gt;. When using the &lt;a href="https://cosmin.us/deploying-the-vmware-cloud-foundation-installer-appliance-step-by-step-instructions/"&gt;VCF Installer appliance&lt;/a&gt;, the same action is called a &lt;strong&gt;Converge&lt;/strong&gt;. This post covers the Import workflow.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before starting, make sure your existing vSphere environment meets the following requirements. Missing any of these will cause the prechecks to fail.&lt;/p&gt;
&lt;h3 id="minimum-versions"&gt;Minimum Versions&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Component&lt;/th&gt;
					&lt;th&gt;Minimum Version&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;VCF Instance&lt;/td&gt;
					&lt;td&gt;9.0.x&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;vCenter&lt;/td&gt;
					&lt;td&gt;8.0 Update 1 or later&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ESXi Hosts&lt;/td&gt;
					&lt;td&gt;8.0 Update 1 or later&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;NSX Manager (if existing)&lt;/td&gt;
					&lt;td&gt;4.1.0.2 or later&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="infrastructure-requirements"&gt;Infrastructure Requirements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shared Datastore&lt;/strong&gt; — At least one datastore shared across all hosts in the cluster, accessible and writable by all hosts. VCF supports vSAN, NFS v3, VMFS, NFS 4.1, iSCSI, and vVols. If multiple datastore types are present, VCF determines the principal storage using this priority: vSAN &amp;gt; NFS v3 &amp;gt; VMFS &amp;gt; NFS 4.1 &amp;gt; iSCSI &amp;gt; vVols.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vSphere Distributed Switch (VDS)&lt;/strong&gt; — Version 8.0 or later with at least 2 uplinks attached from all hosts. Standard switches alone are not supported — if you are still using a VSS, migrate to VDS first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DRS&lt;/strong&gt; — Must be set to &lt;strong&gt;Fully Automated&lt;/strong&gt;. Partially automated or manual DRS is not supported for import. You can change this in the cluster settings before starting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static VMkernel IPs&lt;/strong&gt; — All VMkernel interfaces must use statically assigned IP addresses (except NSX Host TEPs, which can use DHCP).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dedicated vMotion Network&lt;/strong&gt; — vMotion traffic must be on its own dedicated network, not shared with management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS Records&lt;/strong&gt; — Forward and reverse DNS records must be configured for all VCF management components. All FQDNs must resolve to unique IPs. If deploying a new NSX instance, you will need 4 DNS records and 3 reserved IPs for the NSX cluster.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSH Enabled&lt;/strong&gt; — SSH must be enabled on the vCenter being imported.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-is-not-supported"&gt;What Is NOT Supported&lt;/h3&gt;
&lt;p&gt;Before you start, be aware of these blockers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cisco virtual switches&lt;/li&gt;
&lt;li&gt;vCenter instances without a VDS&lt;/li&gt;
&lt;li&gt;Custom vCenter ports for client connections&lt;/li&gt;
&lt;li&gt;Enhanced Linked Mode (ELM) — you must break ELM before importing (see &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-0/fleet-management/what-is/points-to-consider-while-setting-up-vmware-cloud-foundation-sso/how-to-deactivate-enhanced-link-mode-in-vcenter-without-downtime.html"&gt;Broadcom TechDocs&lt;/a&gt; for the deactivation procedure)&lt;/li&gt;
&lt;li&gt;Clusters managed by Dell VxRail&lt;/li&gt;
&lt;li&gt;Multiple NSX Manager instances on a single vCenter&lt;/li&gt;
&lt;li&gt;NSX Bare Metal Edge nodes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="supported-with-caveats"&gt;Supported with Caveats&lt;/h3&gt;
&lt;p&gt;These configurations are supported but require preparation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Baseline-based lifecycle management&lt;/strong&gt; — Clusters using baselines can be imported, but you will get a warning during prechecks. SDDC Manager will take over lifecycle management post-import, and you should plan to convert to vSphere Lifecycle Manager images before upgrading to ESXi 9.0.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vCenter VM on the imported cluster&lt;/strong&gt; — If the vCenter appliance VM lives on one of the clusters being imported, this is supported. However, if it resides on the default management domain cluster, you will need to vMotion it to a supported location first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standalone or single-host clusters&lt;/strong&gt; — Supported only if your vCenter has an additional cluster that meets the import requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="step-1--initiate-the-import-from-vcf-operations"&gt;Step 1 — Initiate the Import from VCF Operations&lt;/h2&gt;
&lt;p&gt;All import actions are performed from the VCF Operations 9 console.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to the &lt;strong&gt;VCF Operations&lt;/strong&gt; home page.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Inventory&lt;/strong&gt; &amp;gt; &lt;strong&gt;Detailed View&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Expand &lt;strong&gt;VCF Instances&lt;/strong&gt; and select the Organization (VCF Instance) into which you want to import the workload domain.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Add Workload Domain&lt;/strong&gt; dropdown and select &lt;strong&gt;Import a vCenter&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1775013926814.webp" alt="‘Inventory’ section in VMware Cloud Foundation Operations, with options for managing VCF instances and importing workloads like vCenters." width="2256" height="922" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-2--define-the-workload-domain"&gt;Step 2 — Define the Workload Domain&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Enter a &lt;strong&gt;unique name&lt;/strong&gt; for the new Workload Domain (e.g., &lt;code&gt;WD-Prod-DC2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="step-3--specify-the-vcenter"&gt;Step 3 — Specify the vCenter&lt;/h2&gt;
&lt;p&gt;You have two options here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Select an existing standalone vCenter&lt;/strong&gt; — If the vCenter is already visible in the VCF inventory, select it from the table.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Specify an external vCenter&lt;/strong&gt; — For a vCenter that is not yet registered, enter the following:
&lt;ul&gt;
&lt;li&gt;vCenter Server FQDN&lt;/li&gt;
&lt;li&gt;vCenter Server Root Password&lt;/li&gt;
&lt;li&gt;SSO Username&lt;/li&gt;
&lt;li&gt;SSO Password&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the vCenter is connected to an existing NSX Manager instance, toggle the NSX option and provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NSX Manager VIP FQDN&lt;/li&gt;
&lt;li&gt;NSX Administrator Password&lt;/li&gt;
&lt;li&gt;NSX Root Password&lt;/li&gt;
&lt;li&gt;NSX Audit Password&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your existing NSX Manager is version 9.0 or later and has Edge clusters, VCF will discover and import the Edge node VMs, including their credentials. The passwords will be reset during import — you can retrieve the new ones from the VCF credential store afterward.&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;Confirm the certificate thumbprints&lt;/strong&gt; for vCenter and NSX Manager (if applicable) and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="step-4--run-prechecks"&gt;Step 4 — Run Prechecks&lt;/h2&gt;
&lt;p&gt;Once you click Next, VCF Operations will run a series of automated prechecks against your environment. These verify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network connectivity and port requirements&lt;/li&gt;
&lt;li&gt;DNS resolution for all FQDNs&lt;/li&gt;
&lt;li&gt;DRS configuration&lt;/li&gt;
&lt;li&gt;Datastore accessibility&lt;/li&gt;
&lt;li&gt;VDS compatibility&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: You may see warnings related to upgrade policy settings if you are importing an 8.x environment. These are informational and typically allow you to resume. If you see errors, resolve them before proceeding.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="step-5--configure-nsx-networking"&gt;Step 5 — Configure NSX Networking&lt;/h2&gt;
&lt;p&gt;If your vCenter is &lt;strong&gt;not&lt;/strong&gt; already connected to an NSX instance, VCF will need to deploy one. For a vCenter 8.x import, NSX 4.x is required, which means a &lt;strong&gt;3-node NSX Manager cluster&lt;/strong&gt; (High-Availability deployment).&lt;/p&gt;
&lt;h3 id="nsx-manager-cluster-configuration"&gt;NSX Manager Cluster Configuration&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Setting&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Deployment Size&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;High-Availability (3 nodes, recommended) — Standard (single node) is only available for vCenter 9.0+ imports&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Appliance Size&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Medium, Large, or Extra Large depending on your environment size&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Appliance FQDNs&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;3 unique FQDNs for each NSX Manager node&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Cluster FQDN&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;The VIP FQDN for the NSX Manager cluster&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Network&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;The NSX Manager nodes will deploy on the same management network as the vCenter&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="passwords"&gt;Passwords&lt;/h3&gt;
&lt;p&gt;Set passwords for the NSX Administrator, Root, and Audit accounts. The password requirements are strict:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Minimum 12 characters&lt;/li&gt;
&lt;li&gt;At least one uppercase, one lowercase, one digit, one special character&lt;/li&gt;
&lt;li&gt;At least 5 different characters&lt;/li&gt;
&lt;li&gt;No dictionary words, no palindromes, no more than 4 sequential characters&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nsx-overlay-networking"&gt;NSX Overlay Networking&lt;/h3&gt;
&lt;p&gt;Choose whether to use the existing management VMkernel for NSX overlay networking or to configure a dedicated overlay transport. For most production environments, keeping the default overlay options is reasonable for the initial import — you can reconfigure overlay networking after the import is complete.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-6--download-required-binaries"&gt;Step 6 — Download Required Binaries&lt;/h2&gt;
&lt;p&gt;This is a step that can catch you off guard. During validation, you may encounter errors indicating that specific NSX binaries are not available.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Fleet Management&lt;/strong&gt; in the VCF Operations console.&lt;/li&gt;
&lt;li&gt;In the version dropdown, select &lt;strong&gt;VCF 5.2&lt;/strong&gt; (yes, 5.2 — this is the version that maps to NSX 4.x binaries for 8.x vCenter imports).&lt;/li&gt;
&lt;li&gt;Look inside &lt;strong&gt;Patch Binaries&lt;/strong&gt; for the specific NSX version required.&lt;/li&gt;
&lt;li&gt;Start the download and wait for it to complete before re-running validation.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Important: This step is version-specific. The binary version required depends on your target NSX version, which is determined by the vCenter version being imported. Check the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-0.html"&gt;VCF 9 Bill of Materials&lt;/a&gt; if you are unsure which binary to download.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="step-7--validate-and-review"&gt;Step 7 — Validate and Review&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Run the &lt;strong&gt;final validation&lt;/strong&gt; — this verifies all networking, binaries, and configuration.&lt;/li&gt;
&lt;li&gt;Review the full configuration summary.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Finish&lt;/strong&gt; to start the import.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The import process will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy the NSX Manager cluster (if no existing NSX was specified)&lt;/li&gt;
&lt;li&gt;Configure networking and overlay transport&lt;/li&gt;
&lt;li&gt;Register all clusters and hosts with SDDC Manager&lt;/li&gt;
&lt;li&gt;Add the workload domain to your VCF Organization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can monitor the progress under &lt;strong&gt;Tasks&lt;/strong&gt; in the &lt;strong&gt;Fleet Management&lt;/strong&gt; section. Once complete, the new workload domain will appear in your Organization inventory.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="post-import-considerations"&gt;Post-Import Considerations&lt;/h2&gt;
&lt;p&gt;After the import completes, there are a few things to address:&lt;/p&gt;
&lt;h3 id="lifecycle-management"&gt;Lifecycle Management&lt;/h3&gt;
&lt;p&gt;SDDC Manager now manages lifecycle for the imported workload domain. If your clusters were using &lt;strong&gt;baselines&lt;/strong&gt; for host patching, plan to convert them to &lt;strong&gt;vSphere Lifecycle Manager images&lt;/strong&gt; before upgrading to ESXi 9.0. See the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/8-0/managing-host-and-cluster-lifecycle-8-0/using-images-to-install-and-update-esxi-hosts-and-clusters/switching-from-baselines-to-images.html"&gt;Broadcom TechDocs on converting from baselines to images&lt;/a&gt; for the procedure.&lt;/p&gt;
&lt;h3 id="password-management"&gt;Password Management&lt;/h3&gt;
&lt;p&gt;ESXi host root credentials for imported hosts may not automatically appear in the VCF credential store. If you need SDDC Manager to manage these passwords, follow the workaround in &lt;a href="https://knowledge.broadcom.com/external/article/388859/add-missing-esxi-root-credentials-into-s.html"&gt;Broadcom KB 388859&lt;/a&gt; to add the missing credential entries.&lt;/p&gt;
&lt;h3 id="upgrading-to-vcf-90x"&gt;Upgrading to VCF 9.0.x&lt;/h3&gt;
&lt;p&gt;Once the 8.x environment is imported, you can plan an upgrade path to bring the vCenter and ESXi hosts up to 9.0.x. This is a separate workflow managed through SDDC Manager and is covered in detail in the &lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-0/deployment/upgrading-cloud-foundation.html"&gt;Broadcom TechDocs upgrade guides&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="quick-reference-import-checklist"&gt;Quick Reference: Import Checklist&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Item&lt;/th&gt;
					&lt;th&gt;Status&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;vCenter 8.0 U1+ with SSH enabled&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ESXi hosts 8.0 U1+&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;VDS 8.0+ with 2+ uplinks&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DRS set to Fully Automated&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Static VMkernel IPs&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Dedicated vMotion network&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Forward + reverse DNS records for all components&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4 DNS records + 3 IPs reserved for NSX cluster&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;No ELM, no Cisco switches, no VxRail&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Required NSX binaries downloaded in Fleet Management&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Shared datastore accessible from all hosts&lt;/td&gt;
					&lt;td&gt;☐&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="references-vcf-9"&gt;References (VCF 9)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-0/building-your-private-cloud-infrastructure/working-with-workload-domains/import-an-existing-vcenter-to-create-a-workload-domain.html"&gt;Broadcom TechDocs — Import an Existing vCenter to Create a Workload Domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-0/building-your-private-cloud-infrastructure/working-with-workload-domains.html"&gt;Broadcom TechDocs — Managing VCF Domains in VMware Cloud Foundation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://knowledge.broadcom.com/external/article/429205"&gt;Broadcom KB 429205 — VCF Import Supported Configurations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://khizeran.com/2025/10/22/importing-a-vcenter-8-x-as-a-workload-domain-into-a-vcf-9-0-instance/"&gt;Sargon Khizeran — Importing a vCenter 8.x as a Workload Domain into a VCF 9.0 Instance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Using 90 Days of Usage for Rightsizing Recommendations in VCF Operations 9</title><link>https://cosmin.us/using-90-days-of-usage-for-rightsizing-recommendations-in-vcf-operations-9/</link><pubDate>Mon, 30 Mar 2026 15:30:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/using-90-days-of-usage-for-rightsizing-recommendations-in-vcf-operations-9/</guid><description>&lt;p&gt;In a &lt;a href="https://cosmin.us/configuring-vcf-operations-9-capacity-calculations-to-consider-90-days-of-usage/"&gt;previous post&lt;/a&gt; I walked through configuring VCF Operations 9 capacity policies to align with a 90-day planning window. If you followed that guide, the good news is that &lt;strong&gt;rightsizing recommendations share the same underlying policy&lt;/strong&gt; — Risk Level, Time Remaining thresholds, and buffers all feed directly into the rightsizing engine.&lt;/p&gt;
&lt;p&gt;So why a separate post? Because rightsizing is a different workflow with its own nuances. Capacity planning asks &amp;ldquo;when will my clusters run out of room?&amp;rdquo; Rightsizing asks &amp;ldquo;are my individual VMs sized correctly for what they actually use?&amp;rdquo; The policy is the same foundation, but how you consume and act on the results is completely different.&lt;/p&gt;</description><content:encoded>&lt;p&gt;In a &lt;a href="https://cosmin.us/configuring-vcf-operations-9-capacity-calculations-to-consider-90-days-of-usage/"&gt;previous post&lt;/a&gt; I walked through configuring VCF Operations 9 capacity policies to align with a 90-day planning window. If you followed that guide, the good news is that &lt;strong&gt;rightsizing recommendations share the same underlying policy&lt;/strong&gt; — Risk Level, Time Remaining thresholds, and buffers all feed directly into the rightsizing engine.&lt;/p&gt;
&lt;p&gt;So why a separate post? Because rightsizing is a different workflow with its own nuances. Capacity planning asks &amp;ldquo;when will my clusters run out of room?&amp;rdquo; Rightsizing asks &amp;ldquo;are my individual VMs sized correctly for what they actually use?&amp;rdquo; The policy is the same foundation, but how you consume and act on the results is completely different.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note on naming: VCF Operations 9 is the successor to VMware Aria Operations (formerly vRealize Operations). The same rightsizing concepts apply across versions but some UI labels may differ.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="how-the-rightsizing-engine-uses-your-90-day-policy"&gt;How the Rightsizing Engine Uses Your 90-Day Policy&lt;/h2&gt;
&lt;p&gt;When you configured your capacity policy with a &lt;strong&gt;Conservative&lt;/strong&gt; risk level and a &lt;strong&gt;90-day critical threshold&lt;/strong&gt;, you also configured the rightsizing engine. Here is how those settings translate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Risk Level = Conservative&lt;/strong&gt; → The rightsizing engine uses the &lt;strong&gt;upper bound&lt;/strong&gt; of projected VM utilization. This means it will not recommend downsizing a VM unless it is genuinely underutilized even during peak periods within your 90-day window. This prevents the classic &amp;ldquo;we rightsized the VM and now month-end processing is slow&amp;rdquo; problem.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Time Remaining thresholds&lt;/strong&gt; → The &amp;ldquo;Recommended Size&amp;rdquo; metric is calculated to ensure the VM maintains a healthy state for the duration defined by the &lt;strong&gt;Green Time Remaining Score Threshold&lt;/strong&gt;, plus an additional 30 days. With a 90-day critical threshold, the engine is projecting VM resource needs well beyond the immediate term.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Peak Focused = Enabled&lt;/strong&gt; → The engine accounts for utilization spikes in its recommendations. A VM that idles at 10% CPU for 85 days but spikes to 80% during month-end will not be flagged as massively oversized.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have not yet configured these policy settings, start with the &lt;a href="https://cosmin.us/configuring-vcf-operations-9-capacity-calculations-to-consider-90-days-of-usage/"&gt;capacity policy guide&lt;/a&gt; first — those same settings power rightsizing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="whats-different-for-rightsizing-business-hours"&gt;What&amp;rsquo;s Different for Rightsizing: Business Hours&lt;/h2&gt;
&lt;p&gt;One policy setting that is particularly important for rightsizing but less so for cluster capacity is &lt;strong&gt;Business Hours&lt;/strong&gt;. Where capacity planning looks at the cluster as a whole around the clock, rightsizing focuses on individual VM behavior — and many VMs have workloads that only run during specific hours.&lt;/p&gt;
&lt;p&gt;If your line-of-business applications are only active during 8 AM – 6 PM on weekdays, you want the rightsizing engine to project demand based on those hours, not the idle weekend utilization that would drag the average down and lead to undersized recommendations.&lt;/p&gt;
&lt;p&gt;To configure Business Hours:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open your policy in &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policies&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policy Definition&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select your production policy (e.g., &lt;code&gt;Production Clusters – 90 Day Capacity Window&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Capacity&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Look for the &lt;strong&gt;Business Hours&lt;/strong&gt; setting.&lt;/li&gt;
&lt;li&gt;Define your operational hours (e.g., Monday–Friday, 08:00–18:00).&lt;/li&gt;
&lt;li&gt;Save the policy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With Business Hours enabled, the capacity engine and rightsizing engine will weight the utilization data from those hours more heavily, which directly improves the accuracy of the recommendations for workloads that follow a predictable schedule.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: If you have 24/7 workloads (databases, web servers), keep Business Hours disabled for those VMs by applying a separate policy without Business Hours defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="understanding-the-recommendation-caps"&gt;Understanding the Recommendation Caps&lt;/h2&gt;
&lt;p&gt;Before you act on any rightsizing results, it is important to understand that VCF Operations applies built-in safety limits to its recommendations:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Direction&lt;/th&gt;
					&lt;th&gt;Cap&lt;/th&gt;
					&lt;th&gt;What it Means&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Oversized&lt;/strong&gt; (downsize)&lt;/td&gt;
					&lt;td&gt;50% of current allocation&lt;/td&gt;
					&lt;td&gt;The engine will never recommend reducing a VM&amp;rsquo;s CPU or Memory by more than half in a single cycle&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Undersized&lt;/strong&gt; (upsize)&lt;/td&gt;
					&lt;td&gt;100% of current allocation&lt;/td&gt;
					&lt;td&gt;The engine will never recommend more than doubling the current configuration in a single cycle&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These caps exist to prevent drastic changes that could destabilize workloads. If a VM is severely oversized — for example, allocated 16 vCPUs but only ever using 2 — the tool will recommend reducing to 8 vCPUs on the first pass. After the change is applied and another 90-day observation period passes, it may recommend a further reduction.&lt;/p&gt;
&lt;p&gt;This is why rightsizing is an &lt;strong&gt;iterative process&lt;/strong&gt;, not a one-time event. Plan for at least two or three cycles to fully right-size an environment that has significant sprawl.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="consuming-the-rightsizing-results"&gt;Consuming the Rightsizing Results&lt;/h2&gt;
&lt;p&gt;Now that your policy is tuned for 90 days of usage data, here is how to find and act on the recommendations.&lt;/p&gt;
&lt;h3 id="finding-oversized-and-undersized-vms"&gt;Finding Oversized and Undersized VMs&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Capacity&lt;/strong&gt; &amp;gt; &lt;strong&gt;Optimize&lt;/strong&gt; &amp;gt; &lt;strong&gt;Rightsize&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The view will show VMs flagged as &lt;strong&gt;Oversized&lt;/strong&gt;, &lt;strong&gt;Undersized&lt;/strong&gt;, or &lt;strong&gt;Powered Off&lt;/strong&gt; based on your policy settings.&lt;/li&gt;
&lt;li&gt;Filter by cluster or custom group to focus on your production workloads.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="key-metrics-to-review"&gt;Key Metrics to Review&lt;/h3&gt;
&lt;p&gt;For each VM, pay attention to these metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recommended vCPUs&lt;/strong&gt; — The engine&amp;rsquo;s suggestion based on 90 days of projected peak demand (Conservative) or average demand (Aggressive).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recommended Memory&lt;/strong&gt; — Same projection logic applied to RAM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU Demand %&lt;/strong&gt; — What the VM is actually using relative to what is allocated. A VM with 8 vCPUs at 5% demand is a strong downsizing candidate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory Active %&lt;/strong&gt; — Active memory versus allocated. Be cautious here — memory rightsizing is more sensitive than CPU because memory pressure can cause ballooning or swapping.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="exporting-rightsizing-data-for-quarterly-reviews"&gt;Exporting Rightsizing Data for Quarterly Reviews&lt;/h3&gt;
&lt;p&gt;For quarterly capacity reviews, export the rightsizing recommendations directly from the &lt;strong&gt;Rightsize&lt;/strong&gt; page:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Capacity&lt;/strong&gt; &amp;gt; &lt;strong&gt;Optimize&lt;/strong&gt; &amp;gt; &lt;strong&gt;Rightsize&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Apply your filters (cluster, custom group, or object type) to scope the results to your production workloads.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Export&lt;/strong&gt; option to download the rightsizing data as a CSV or PDF.&lt;/li&gt;
&lt;li&gt;For a recurring view, use the out-of-the-box &lt;strong&gt;VM Rightsizing Details&lt;/strong&gt; dashboard — search for it under &lt;strong&gt;Dashboards&lt;/strong&gt; — which provides a consolidated view of potential savings and reclaimable capacity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This export becomes the artifact you bring to your quarterly capacity review meeting, alongside the capacity projections from the first blog post.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-rightsizing-workflow-a-practical-approach"&gt;The Rightsizing Workflow: A Practical Approach&lt;/h2&gt;
&lt;p&gt;Rightsizing an entire environment in one pass is risky. Here is a phased approach that works well with a 90-day observation window:&lt;/p&gt;
&lt;h3 id="phase-1--identify-quick-wins-week-12"&gt;Phase 1 — Identify Quick Wins (Week 1–2)&lt;/h3&gt;
&lt;p&gt;Focus on the obvious outliers first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VMs with &lt;strong&gt;CPU Demand &amp;lt; 10%&lt;/strong&gt; and &lt;strong&gt;8+ vCPUs&lt;/strong&gt; allocated.&lt;/li&gt;
&lt;li&gt;VMs with &lt;strong&gt;Memory Active &amp;lt; 20%&lt;/strong&gt; and &lt;strong&gt;16+ GB&lt;/strong&gt; allocated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Powered-off VMs&lt;/strong&gt; that have been off for 30+ days — these are candidates for decommissioning, not rightsizing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="phase-2--apply-cpu-changes-first-week-34"&gt;Phase 2 — Apply CPU Changes First (Week 3–4)&lt;/h3&gt;
&lt;p&gt;CPU rightsizing is generally safer than memory rightsizing because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;vCPU changes can often be performed with a scheduled reboot.&lt;/li&gt;
&lt;li&gt;The performance impact of reducing vCPUs is more predictable.&lt;/li&gt;
&lt;li&gt;Over-allocation of vCPUs can actually &lt;em&gt;hurt&lt;/em&gt; performance due to CPU scheduling overhead (co-stop and ready time).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apply the recommended CPU changes to your quick-win VMs and let the environment run for at least one full collection cycle before moving on.&lt;/p&gt;
&lt;h3 id="phase-3--evaluate-memory-changes-week-5"&gt;Phase 3 — Evaluate Memory Changes (Week 5+)&lt;/h3&gt;
&lt;p&gt;Memory is trickier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a VM loses memory headroom, it may start ballooning or swapping, which causes latent performance issues that do not show up immediately.&lt;/li&gt;
&lt;li&gt;Always leave a 10–15% memory buffer above the recommended amount.&lt;/li&gt;
&lt;li&gt;Monitor the &lt;strong&gt;Balloon&lt;/strong&gt; and &lt;strong&gt;Swap Used&lt;/strong&gt; metrics for at least a week after making changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="phase-4--observe-and-iterate-90-days"&gt;Phase 4 — Observe and Iterate (90 Days)&lt;/h3&gt;
&lt;p&gt;After the initial round of changes, let the environment run for a full 90-day cycle. The engine will re-evaluate the VMs based on their new configuration and may recommend further adjustments. This is normal — rightsizing is iterative by design.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="bringing-it-all-together"&gt;Bringing It All Together&lt;/h2&gt;
&lt;p&gt;If you configured your capacity policy using the &lt;a href="https://cosmin.us/configuring-vcf-operations-9-capacity-calculations-to-consider-90-days-of-usage/"&gt;previous guide&lt;/a&gt;, you already have 90% of the rightsizing foundation in place. The additional steps for rightsizing are:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Setting&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
					&lt;th&gt;Notes&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Risk Level&lt;/td&gt;
					&lt;td&gt;Conservative&lt;/td&gt;
					&lt;td&gt;Same as capacity policy — shared setting&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Time Remaining Thresholds&lt;/td&gt;
					&lt;td&gt;Warning: 120 days, Critical: 90 days&lt;/td&gt;
					&lt;td&gt;Same as capacity policy — shared setting&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Peak Focused&lt;/td&gt;
					&lt;td&gt;Enabled&lt;/td&gt;
					&lt;td&gt;Same as capacity policy — shared setting&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Business Hours&lt;/td&gt;
					&lt;td&gt;Defined per workload type&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Rightsizing-specific&lt;/strong&gt; — critical for accuracy&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Recommendation Caps&lt;/td&gt;
					&lt;td&gt;50% down / 100% up&lt;/td&gt;
					&lt;td&gt;Built-in, not configurable&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Rightsizing Approach&lt;/td&gt;
					&lt;td&gt;Iterative (2–3 cycles)&lt;/td&gt;
					&lt;td&gt;Plan for 6–9 months of refinement&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="why-this-matters"&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;Every oversized VM is wasted capacity that could defer a hardware purchase. Every undersized VM is a performance risk that could lead to an outage.&lt;/p&gt;
&lt;p&gt;With a 90-day rightsizing model you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reclaim capacity&lt;/strong&gt; without guessing — recommendations are backed by 90 days of real usage data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid month-end surprises&lt;/strong&gt; — Conservative + Peak Focused ensures cyclical workloads are respected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build a repeatable process&lt;/strong&gt; — quarterly rightsizing reviews become a standard part of your capacity management cadence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quantify savings&lt;/strong&gt; — the delta between current allocation and recommended allocation is the capacity you can reclaim or redeploy.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="references-vcf-9"&gt;References (VCF 9)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vmware.com/us/en/vmware-cis/aria/aria-operations/9-0/vmware-aria-operations-configuration-guide-9-0/optimizing-capacity-and-improving-performance.html"&gt;Broadcom TechDocs — Optimizing Capacity and Improving Performance (VCF Operations 9.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.vmware.com/us/en/vmware-cis/aria/aria-operations/9-0/vmware-aria-operations-configuration-guide-9-0/configuring-policies.html"&gt;Broadcom TechDocs — Configuring Policies (VCF Operations 9.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.brockpeterson.com/post/vm-rightsizing-minimums-in-vcf-operations"&gt;Brock Peterson — VM Rightsizing Minimums in VCF Operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.brockpeterson.com/post/finding-capacity-with-vcf-operations"&gt;Brock Peterson — Finding Capacity with VCF Operations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Configuring VCF Operations 9 Capacity Calculations to Consider 90 Days of Usage</title><link>https://cosmin.us/configuring-vcf-operations-9-capacity-calculations-to-consider-90-days-of-usage/</link><pubDate>Mon, 30 Mar 2026 11:00:00 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/configuring-vcf-operations-9-capacity-calculations-to-consider-90-days-of-usage/</guid><description>&lt;p&gt;As a solutions architect managing a VCF 9 Operations instance, one of the first things I tune after initial deployment is the &lt;strong&gt;capacity engine&lt;/strong&gt;. Out of the box, VCF Operations uses an exponential decay model that heavily weights the most recent data points. That is great for catching sudden spikes, but it can misrepresent your true capacity posture if your environment has cyclical workloads — think month-end batch jobs, quarterly reporting bursts, or seasonal traffic patterns.&lt;/p&gt;</description><content:encoded>&lt;p&gt;As a solutions architect managing a VCF 9 Operations instance, one of the first things I tune after initial deployment is the &lt;strong&gt;capacity engine&lt;/strong&gt;. Out of the box, VCF Operations uses an exponential decay model that heavily weights the most recent data points. That is great for catching sudden spikes, but it can misrepresent your true capacity posture if your environment has cyclical workloads — think month-end batch jobs, quarterly reporting bursts, or seasonal traffic patterns.&lt;/p&gt;
&lt;p&gt;If your planning cycles are based on &lt;strong&gt;90 days of usage&lt;/strong&gt;, you need to make sure the capacity engine&amp;rsquo;s projections actually reflect that window. In this post I will walk through the policy settings that influence how VCF Operations calculates capacity, and how to configure them so 90 days of historical utilization are meaningfully represented in the results.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note on naming: VCF Operations 9 is the successor to VMware Aria Operations (formerly vRealize Operations). If you are &lt;a href="https://cosmin.us/upgrading-aria-operations-to-9-0-via-aria-suite-lifecycle/"&gt;upgrading from Aria Operations&lt;/a&gt;, the same policy concepts apply but some UI labels may differ.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="why-90-days-matters"&gt;Why 90 Days Matters&lt;/h2&gt;
&lt;p&gt;Most enterprise procurement and capacity planning processes operate on a &lt;strong&gt;quarterly cadence&lt;/strong&gt;. When leadership asks &amp;ldquo;do we need to buy more hosts next quarter?&amp;rdquo;, the answer needs to be rooted in at least 90 days of observed demand — not just the last week of usage.&lt;/p&gt;
&lt;p&gt;By default, the VCF Operations capacity engine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uses an &lt;strong&gt;exponential decay window&lt;/strong&gt; for historical data, which means it considers all available history but gives significantly more weight to recent data points.&lt;/li&gt;
&lt;li&gt;Projects &lt;strong&gt;1 year into the future&lt;/strong&gt; for trend analysis.&lt;/li&gt;
&lt;li&gt;Calculates &lt;strong&gt;Capacity Remaining&lt;/strong&gt; based on the projected peak usage over the next &lt;strong&gt;3 days&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Determines &lt;strong&gt;Recommended Size&lt;/strong&gt; by looking at the maximum projected utilization from now until &lt;strong&gt;30 days past the warning threshold&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem? If you had a one-time spike last Tuesday, the decay model may over-represent that event. Conversely, if you had a massive batch processing window 60 days ago that will repeat, the decay model may under-represent it. We need to tune the policy so the engine balances both recent and historical data appropriately.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-1--clone-your-base-policy"&gt;Step 1 — Clone Your Base Policy&lt;/h2&gt;
&lt;p&gt;Never modify the default vSphere Solution policy directly. Always clone it first so your customizations survive product upgrades.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policies&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policy Definition&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;ADD&lt;/strong&gt; to create a new policy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774897653195.webp" alt="VMware Cloud Foundation Policy Definition screen with options for adding and managing policies, including Default Policy marked as active." width="1934" height="1224" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Select &lt;strong&gt;vSphere Solution&amp;rsquo;s Default Policy (VCF Operations)&lt;/strong&gt; as the base policy to inherit from.&lt;/li&gt;
&lt;li&gt;Name it something descriptive, e.g., &lt;code&gt;Production Clusters – 90 Day Capacity Window&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;CREATE POLICY&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774897811449.webp" alt="UI step showing “Create New Policy” with fields for name and description, inheriting from Default Policy." width="1404" height="772" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: Create separate policies for production and non-production workloads. Production should use conservative settings while dev/test can safely use aggressive settings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="step-2--configure-the-capacity-settings"&gt;Step 2 — Configure the Capacity Settings&lt;/h2&gt;
&lt;p&gt;Once inside the policy, navigate to the &lt;strong&gt;Capacity&lt;/strong&gt; settings section. This is where we align the engine with our 90-day planning horizon.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774897940093.webp" alt="“Production Clusters – 90 Day Capacity Window” under Configurations/Policy Definition. Metrics/Alerts/Capacity sections are empty." width="2044" height="946" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="risk-level-conservative-vs-aggressive"&gt;Risk Level: Conservative vs. Aggressive&lt;/h3&gt;
&lt;p&gt;This setting directly affects how the engine interprets historical data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Conservative&lt;/strong&gt; — Uses the &lt;strong&gt;upper bound&lt;/strong&gt; of the projected utilization range. This means the engine will factor in higher peaks from your 90-day history and project them forward. &lt;strong&gt;Use this for production.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aggressive&lt;/strong&gt; — Uses the &lt;strong&gt;mean&lt;/strong&gt; of the upper and lower bound projections. This smooths out peaks but can underestimate future demand. Use this for dev/test environments where higher utilization is acceptable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774898052762.webp" alt="VCF Operations capacity risk level set to Conservative for production clusters" width="2488" height="1426" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;For a 90-day capacity planning model, set this to &lt;strong&gt;Conservative&lt;/strong&gt;. This ensures that the periodic spikes captured within that 90-day window are not averaged away.&lt;/p&gt;
&lt;h3 id="time-remaining-thresholds"&gt;Time Remaining Thresholds&lt;/h3&gt;
&lt;p&gt;These thresholds define when VCF Operations triggers capacity alerts. Align them with your 90-day planning cycle:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Threshold&lt;/th&gt;
					&lt;th&gt;Recommended Value&lt;/th&gt;
					&lt;th&gt;Rationale&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;120 days&lt;/td&gt;
					&lt;td&gt;Gives you a full quarter plus a 30-day buffer to initiate procurement&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Critical&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;90 days&lt;/td&gt;
					&lt;td&gt;Aligns exactly with your quarterly planning boundary&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Setting the &lt;strong&gt;Critical&lt;/strong&gt; threshold to 90 days means that when the capacity engine projects resource exhaustion within the next 90 days, you will receive a critical alert — directly aligning the tool with your procurement lead time.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774898328626.webp" alt="Capacity window setup for production clusters in VCF Operations 9, focusing on adjusting thresholds to ensure timely alerts for resource exhaustion over 90 days." width="1848" height="1108" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="capacity-buffers"&gt;Capacity Buffers&lt;/h3&gt;
&lt;p&gt;Capacity buffers reserve a percentage of total usable capacity as headroom. These are subtracted from the total before usage percentages are calculated:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Resource&lt;/th&gt;
					&lt;th&gt;Recommended Buffer&lt;/th&gt;
					&lt;th&gt;Notes&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;CPU&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;10–15%&lt;/td&gt;
					&lt;td&gt;Accounts for HA failover and burst headroom&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;10–15%&lt;/td&gt;
					&lt;td&gt;Memory is typically the first resource to constrain&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Disk&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;15–20%&lt;/td&gt;
					&lt;td&gt;vSAN environments need additional headroom for rebalancing operations&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These buffers ensure that when the engine says you have 90 days of capacity remaining, it actually means 90 days with comfortable headroom — not 90 days until the cluster is at 100% and VMs start ballooning.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774898390336.webp" alt="Capacity remaining in a VCF operations setup, with critical, immediate, and warning indicators for buffer levels." width="1154" height="306" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;h3 id="peak-focused-setting"&gt;Peak Focused Setting&lt;/h3&gt;
&lt;p&gt;If your environment has significant periodic peaks (month-end processing, backup windows, etc.), &lt;strong&gt;enable the Peak Focused setting&lt;/strong&gt;. This tells the capacity engine to give higher weight to utilization spikes when projecting future needs. Combined with the Conservative risk level and a 90-day critical threshold, this creates a capacity model that respects your historical peak demand patterns.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774898433584.webp" alt="Capacity projection graph for VCF operations, highlighting a conservative risk level with a focus on peak usage over 90 days." width="1264" height="768" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-3--use-maintenance-schedules-to-protect-your-90-day-baseline"&gt;Step 3 — Use Maintenance Schedules to Protect Your 90-Day Baseline&lt;/h2&gt;
&lt;p&gt;When a non-recurring event is happening in your environment — a one-time data migration, a stress test, a host rebuild — the resulting utilization data will feed into the capacity engine and skew your 90-day projections. VCF Operations provides &lt;strong&gt;Maintenance Schedules&lt;/strong&gt; to prevent this.&lt;/p&gt;
&lt;p&gt;A Maintenance Schedule pauses data collection and &lt;a href="https://cosmin.us/suppress-esxi-host-alerts-in-vcf-operations-9-when-a-host-enters-maintenance-mode/"&gt;suppresses alerts&lt;/a&gt; for selected objects during a defined window. Because the capacity engine cannot project on data it never collected, those anomalous periods are effectively excluded from your 90-day baseline.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Inventory Management&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the object(s) that will be affected by the non-standard activity (e.g., the ESXi hosts involved in a migration).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Start Maintenance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Define the start and end times of the maintenance window, or set it to run indefinitely if you are unsure when the activity will complete and plan to stop it manually.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Tip: If you know the maintenance window in advance, schedule it before the activity begins. The engine will not collect any metrics during that window, keeping your historical data clean for capacity planning.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once the maintenance window ends, data collection resumes automatically and the capacity engine continues its projections based only on valid operational data.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-4--apply-the-policy-to-your-clusters"&gt;Step 4 — Apply the Policy to Your Clusters&lt;/h2&gt;
&lt;p&gt;The policy only takes effect when it is attached to the correct objects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Still in the Policy workspace, navigate to the &lt;strong&gt;Groups and Objects&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Apply the policy to a &lt;strong&gt;Custom Group&lt;/strong&gt; or directly to your production vSphere clusters.&lt;/li&gt;
&lt;li&gt;If you have not created a Custom Group yet, go to &lt;strong&gt;Infrastructure Operations&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Custom Groups&lt;/strong&gt; and create one that includes your production clusters.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Important: Policy precedence matters. If multiple policies apply to the same object, the most specific one wins. Verify your policy is the active one for the target clusters by checking &lt;strong&gt;Policies&lt;/strong&gt; &amp;gt; &lt;strong&gt;Active Policies&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/screenshot-1774898957778.webp" alt="Custom group selection in a capacity window configuration wizard step." width="1062" height="1422" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="step-5--validate-the-configuration"&gt;Step 5 — Validate the Configuration&lt;/h2&gt;
&lt;p&gt;After applying the policy, wait for at least one full collection cycle (typically 5 minutes) and then validate:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Capacity&lt;/strong&gt; &amp;gt; &lt;strong&gt;Optimize&lt;/strong&gt; &amp;gt; &lt;strong&gt;Assess&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select one of your production clusters.&lt;/li&gt;
&lt;li&gt;Review the &lt;strong&gt;Time Remaining&lt;/strong&gt; and &lt;strong&gt;Capacity Remaining&lt;/strong&gt; metrics. They should now reflect the configured thresholds (warning at 120 days, critical at 90 days).&lt;/li&gt;
&lt;li&gt;Check the &lt;strong&gt;Demand Trend&lt;/strong&gt; chart — the projection curve should show a conservative (upper bound) trend line that accounts for historical peaks.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the values look unreasonably aggressive or conservative, revisit your buffer percentages and risk level settings.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="bringing-it-all-together"&gt;Bringing It All Together&lt;/h2&gt;
&lt;p&gt;Here is a summary of the full configuration for a 90-day capacity planning model:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Setting&lt;/th&gt;
					&lt;th&gt;Value&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Risk Level&lt;/td&gt;
					&lt;td&gt;Conservative&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Warning Threshold&lt;/td&gt;
					&lt;td&gt;120 days&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Critical Threshold&lt;/td&gt;
					&lt;td&gt;90 days&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CPU Buffer&lt;/td&gt;
					&lt;td&gt;10–15%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Memory Buffer&lt;/td&gt;
					&lt;td&gt;10–15%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Disk Buffer&lt;/td&gt;
					&lt;td&gt;15–20%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Peak Focused&lt;/td&gt;
					&lt;td&gt;Enabled&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Maintenance Schedules&lt;/td&gt;
					&lt;td&gt;Used during non-standard events&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Policy Applied To&lt;/td&gt;
					&lt;td&gt;Production Clusters (Custom Group)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="why-this-matters-beyond-dashboards"&gt;Why This Matters Beyond Dashboards&lt;/h2&gt;
&lt;p&gt;Getting the capacity engine right is not about making the dashboards look green. It is about answering the question your CTO is going to ask: &lt;strong&gt;&amp;ldquo;Will we run out of capacity before our next hardware refresh?&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With a 90-day model you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Justify hardware purchases&lt;/strong&gt; with data rooted in 90 days of real demand, not just last week&amp;rsquo;s averages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Catch cyclical patterns&lt;/strong&gt; that shorter windows miss — month-end, quarter-end, year-end processing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduce overprovisioning&lt;/strong&gt; by using conservative projections that account for peaks without panic-buying.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Align IT and finance&lt;/strong&gt; by speaking the same quarterly language.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Capacity is only half of the story — I covered making VM-level recommendations honor the same window in &lt;a href="https://cosmin.us/using-90-days-of-usage-for-rightsizing-recommendations-in-vcf-operations-9/"&gt;Using 90 Days of Usage for Rightsizing Recommendations in VCF Operations 9&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="references-vcf-9"&gt;References (VCF 9)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.vmware.com/us/en/vmware-cis/aria/aria-operations/9-0/vmware-aria-operations-configuration-guide-9-0/optimizing-capacity-and-improving-performance.html"&gt;Broadcom TechDocs — Optimizing Capacity and Improving Performance (VCF Operations 9.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.vmware.com/us/en/vmware-cis/aria/aria-operations/9-0/vmware-aria-operations-configuration-guide-9-0/configuring-policies.html"&gt;Broadcom TechDocs — Configuring Policies (VCF Operations 9.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.brockpeterson.com/post/finding-capacity-with-vcf-operations"&gt;Brock Peterson — Finding Capacity with VCF Operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.brockpeterson.com/post/vcf-operations-custom-groups-custom-properties-and-super-metrics"&gt;Brock Peterson — VCF Operations Custom Groups, Custom Properties, and Super Metrics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>How I Migrated My WordPress Blog to Hugo on Cloudflare Workers (And Why You Should Too)</title><link>https://cosmin.us/wordpress-to-hugo-cloudflare-workers-migration/</link><pubDate>Tue, 24 Mar 2026 13:00:00 -0400</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/wordpress-to-hugo-cloudflare-workers-migration/</guid><description>&lt;p&gt;After years of running my tech blog on WordPress, I finally pulled the trigger and migrated everything to &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; — a blazing-fast static site generator — hosted on &lt;a href="https://workers.cloudflare.com/"&gt;Cloudflare Workers&lt;/a&gt;. The result? A site that loads in milliseconds, costs nothing to host, and requires zero server maintenance.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s exactly how I did it, what went wrong, and what I&amp;rsquo;d do differently.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-leave-wordpress"&gt;Why Leave WordPress?&lt;/h2&gt;
&lt;p&gt;WordPress served me well for years. But as my post count grew to 278 articles (mostly VMware VCF guides with dozens of screenshots each), the cracks started showing:&lt;/p&gt;</description><content:encoded>&lt;p&gt;After years of running my tech blog on WordPress, I finally pulled the trigger and migrated everything to &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; — a blazing-fast static site generator — hosted on &lt;a href="https://workers.cloudflare.com/"&gt;Cloudflare Workers&lt;/a&gt;. The result? A site that loads in milliseconds, costs nothing to host, and requires zero server maintenance.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s exactly how I did it, what went wrong, and what I&amp;rsquo;d do differently.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-leave-wordpress"&gt;Why Leave WordPress?&lt;/h2&gt;
&lt;p&gt;WordPress served me well for years. But as my post count grew to 278 articles (mostly VMware VCF guides with dozens of screenshots each), the cracks started showing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Even with caching plugins, page loads were sluggish. Each request hit PHP and MySQL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance overhead&lt;/strong&gt;: WordPress core updates, plugin updates, PHP version bumps, SSL renewals, database backups — it never ends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security surface&lt;/strong&gt;: Every plugin is a potential vulnerability. I was running a firewall plugin just to block brute-force login attempts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Hosting a WordPress site with decent performance isn&amp;rsquo;t free. My VPS bill added up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A static site eliminates all of these problems. No database, no PHP, no server — just HTML files served from a CDN.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="choosing-the-stack"&gt;Choosing the Stack&lt;/h2&gt;
&lt;h3 id="hugo-over-jekyll-gatsby-nextjs"&gt;Hugo over Jekyll, Gatsby, Next.js&lt;/h3&gt;
&lt;p&gt;I evaluated several static site generators:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Generator&lt;/th&gt;
					&lt;th&gt;Pros&lt;/th&gt;
					&lt;th&gt;Cons&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Jekyll&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Ruby-based, GitHub Pages native&lt;/td&gt;
					&lt;td&gt;Slow builds for large sites&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Gatsby&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;React-based, rich plugin ecosystem&lt;/td&gt;
					&lt;td&gt;Heavy, complex, Node dependency hell&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Next.js&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Full-stack capable&lt;/td&gt;
					&lt;td&gt;Overkill for a blog&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Hugo&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Single binary, sub-second builds, Go templating&lt;/td&gt;
					&lt;td&gt;Steeper template learning curve&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Hugo won because of &lt;strong&gt;build speed&lt;/strong&gt;. With 278 posts and 13,000+ images, Hugo builds the entire site in under 7 seconds. Jekyll would take minutes.&lt;/p&gt;
&lt;h3 id="papermod-theme"&gt;PaperMod Theme&lt;/h3&gt;
&lt;p&gt;I went with &lt;a href="https://github.com/adityatelange/hugo-PaperMod/"&gt;PaperMod&lt;/a&gt; — a clean, fast, SEO-optimized Hugo theme. It ships with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dark/light mode toggle&lt;/li&gt;
&lt;li&gt;Built-in search (via Fuse.js)&lt;/li&gt;
&lt;li&gt;Table of contents&lt;/li&gt;
&lt;li&gt;Share buttons&lt;/li&gt;
&lt;li&gt;Reading time estimates&lt;/li&gt;
&lt;li&gt;Archive and taxonomy pages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I customized it with a dark-first infrastructure theme — muted teal accents, JetBrains Mono for metadata, and Inter for body text.&lt;/p&gt;
&lt;h3 id="cloudflare-workers-over-github-pages-netlify-vercel"&gt;Cloudflare Workers over GitHub Pages, Netlify, Vercel&lt;/h3&gt;
&lt;p&gt;Cloudflare Workers (formerly Cloudflare Pages) was the obvious choice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Free tier&lt;/strong&gt; handles my traffic easily&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global CDN&lt;/strong&gt; — content served from 300+ edge locations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git integration&lt;/strong&gt; — push to GitHub, site deploys automatically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom domain + SSL&lt;/strong&gt; — handled natively&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security headers&lt;/strong&gt; — configured via &lt;code&gt;_headers&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="the-migration-process"&gt;The Migration Process&lt;/h2&gt;
&lt;h3 id="step-1-extract-the-wordpress-backup"&gt;Step 1: Extract the WordPress Backup&lt;/h3&gt;
&lt;p&gt;My WordPress backup was in the proprietary &lt;code&gt;.wpress&lt;/code&gt; format (from the All-in-One WP Migration plugin). This isn&amp;rsquo;t a standard ZIP — it has a custom binary header format.&lt;/p&gt;
&lt;p&gt;I wrote a Python script to parse the binary headers and extract the contents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_wpress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HEADER_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;HEADER_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;269&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ... extract file data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This gave me two critical files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;database.sql&lt;/code&gt; — the full WordPress MySQL dump&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uploads/&lt;/code&gt; — all 13,000+ media files&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="step-2-convert-posts-to-markdown"&gt;Step 2: Convert Posts to Markdown&lt;/h3&gt;
&lt;p&gt;The core of the migration was a Python script that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Parsed the SQL dump&lt;/strong&gt; to extract posts (title, content, slug, date, categories, tags, excerpt)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Converted HTML content to Markdown&lt;/strong&gt; using the &lt;code&gt;markdownify&lt;/code&gt; library&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generated Hugo-compatible front matter&lt;/strong&gt; with proper YAML escaping&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rewrote image URLs&lt;/strong&gt; from &lt;code&gt;wp-content/uploads/&lt;/code&gt; to Hugo&amp;rsquo;s &lt;code&gt;/images/&lt;/code&gt; path&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The trickiest part was &lt;strong&gt;YAML front matter sanitization&lt;/strong&gt;. WordPress post titles and descriptions contain all sorts of special characters — quotes, ampersands, colons, tabs — that break YAML parsing. I wrote a &lt;code&gt;yaml_safe_string()&lt;/code&gt; helper:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;yaml_safe_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;:&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;[]&amp;amp;*?|&amp;gt;!%@`&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="s1"&gt;,#&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;escaped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;escaped&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-3-scaffold-the-hugo-site"&gt;Step 3: Scaffold the Hugo Site&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo new site cosmin-gq
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; cosmin-gq
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I configured &lt;code&gt;hugo.toml&lt;/code&gt; with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SEO metadata (description, keywords, author, Open Graph)&lt;/li&gt;
&lt;li&gt;JSON-LD structured data via a custom &lt;code&gt;extend_head.html&lt;/code&gt; partial&lt;/li&gt;
&lt;li&gt;Sitemap configuration (304 URLs generated)&lt;/li&gt;
&lt;li&gt;RSS feed (278 items)&lt;/li&gt;
&lt;li&gt;Security headers (&lt;code&gt;_headers&lt;/code&gt; file for HSTS, CSP, X-Frame-Options)&lt;/li&gt;
&lt;li&gt;Feed redirects (&lt;code&gt;/feed/&lt;/code&gt; → &lt;code&gt;/index.xml&lt;/code&gt; for WordPress RSS subscribers)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="step-4-deploy-to-cloudflare"&gt;Step 4: Deploy to Cloudflare&lt;/h3&gt;
&lt;p&gt;Cloudflare recently merged Pages into Workers, so the deployment uses Wrangler:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build settings:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build command: &lt;code&gt;hugo --minify&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Deploy command: &lt;code&gt;npx wrangler deploy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;wrangler.jsonc&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cosmin-gq&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;compatibility_date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-09-27&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;assets&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;directory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;public&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The workflow is simple: &lt;code&gt;git push&lt;/code&gt; → Cloudflare clones the repo → Hugo builds → Wrangler uploads 14,000+ static files to the edge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="gotchas-and-lessons-learned"&gt;Gotchas and Lessons Learned&lt;/h2&gt;
&lt;h3 id="1-the-paginate-deprecation"&gt;1. The &lt;code&gt;paginate&lt;/code&gt; Deprecation&lt;/h3&gt;
&lt;p&gt;Hugo v0.128.0+ removed the &lt;code&gt;paginate&lt;/code&gt; config key. Cloudflare&amp;rsquo;s build environment uses a newer Hugo version, so this broke the build:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ERROR deprecated: site config key paginate was deprecated in Hugo v0.128.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Replace &lt;code&gt;paginate = 10&lt;/code&gt; with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pagination&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;pagerSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-cloudflare-workers-dont-support-404-redirects"&gt;2. Cloudflare Workers Don&amp;rsquo;t Support 404 Redirects&lt;/h3&gt;
&lt;p&gt;My &lt;code&gt;_redirects&lt;/code&gt; file had rules to block old WordPress admin paths:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/wp-admin/* /404.html 404
/wp-login.php /404.html 404
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Workers only supports redirect status codes (200, 301, 302, 303, 307, 308). Hugo&amp;rsquo;s built-in 404 page handles missing routes anyway.&lt;/p&gt;
&lt;h3 id="3-the-baseurl-matters"&gt;3. The &lt;code&gt;baseURL&lt;/code&gt; Matters&lt;/h3&gt;
&lt;p&gt;Hugo generates all links using the &lt;code&gt;baseURL&lt;/code&gt; from &lt;code&gt;hugo.toml&lt;/code&gt;. If your production domain isn&amp;rsquo;t set up yet and you&amp;rsquo;re testing on the &lt;code&gt;.workers.dev&lt;/code&gt; URL, all links will point to the wrong place. Set &lt;code&gt;baseURL&lt;/code&gt; to match wherever the site is currently served.&lt;/p&gt;
&lt;h3 id="4-dont-commit-the-public-directory"&gt;4. Don&amp;rsquo;t Commit the &lt;code&gt;public/&lt;/code&gt; Directory&lt;/h3&gt;
&lt;p&gt;Hugo&amp;rsquo;s build output goes into &lt;code&gt;public/&lt;/code&gt;. Since Cloudflare rebuilds this on every deploy, there&amp;rsquo;s no reason to track it in git. Add it to &lt;code&gt;.gitignore&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;public/
.DS_Store
resources/
node_modules/
.wrangler/
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="5-ad-blockers-hide-linkedin-share-buttons"&gt;5. Ad Blockers Hide LinkedIn Share Buttons&lt;/h3&gt;
&lt;p&gt;If you add LinkedIn share buttons, be aware that most ad blockers (uBlock Origin, AdGuard) will hide them because the share URL contains &lt;code&gt;linkedin.com&lt;/code&gt;. Nothing you can do about this from the site side.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-results"&gt;The Results&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Metric&lt;/th&gt;
					&lt;th&gt;WordPress&lt;/th&gt;
					&lt;th&gt;Hugo + Cloudflare&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Build time&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;N/A (dynamic)&lt;/td&gt;
					&lt;td&gt;~7 seconds&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Page load&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;2-4 seconds&lt;/td&gt;
					&lt;td&gt;&amp;lt; 200ms&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Hosting cost&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;~$20/month&lt;/td&gt;
					&lt;td&gt;Free&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Server maintenance&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Weekly&lt;/td&gt;
					&lt;td&gt;None&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Security updates&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Constant&lt;/td&gt;
					&lt;td&gt;None needed&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Uptime&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;99.5%&lt;/td&gt;
					&lt;td&gt;99.99% (Cloudflare SLA)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The site now loads almost instantly from anywhere in the world, costs nothing to run, and the only &amp;ldquo;deployment&amp;rdquo; step is &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-id-do-differently"&gt;What I&amp;rsquo;d Do Differently&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with &lt;code&gt;.gitignore&lt;/code&gt;&lt;/strong&gt; — I accidentally committed the &lt;code&gt;public/&lt;/code&gt; directory on the first push (13,000+ files). Set up &lt;code&gt;.gitignore&lt;/code&gt; before the first commit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;code&gt;relativeURLs&lt;/code&gt;&lt;/strong&gt; — This avoids the &lt;code&gt;baseURL&lt;/code&gt; mismatch issue entirely during testing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test the Cloudflare build first&lt;/strong&gt; — The Workers deployment flow has quirks (no 404 redirects, Wrangler auto-detection). A quick test deploy before migrating all content saves debugging time.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="final-thoughts"&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re running a technical blog on WordPress and it&amp;rsquo;s mostly content + screenshots (no e-commerce, no user accounts, no dynamic forms), there&amp;rsquo;s very little reason to stay on WordPress. Hugo gives you better performance, better security, and zero ongoing costs.&lt;/p&gt;
&lt;p&gt;The migration took about a day for 278 posts and 13,000 images. The hardest part was sanitizing the YAML front matter — everything else was straightforward.&lt;/p&gt;
&lt;p&gt;The site is live at &lt;a href="https://cosmin.us/"&gt;cosmin.us&lt;/a&gt;. All the old content is preserved, SEO metadata is intact, and the RSS feed still works at the same path.&lt;/p&gt;
&lt;p&gt;If you have questions about the migration process, feel free to reach out on &lt;a href="https://x.com/cosm0_"&gt;X (@cosm0_)&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Bulk Delete Alerts in VCF Operations (formerly know as Aria Operations)</title><link>https://cosmin.us/bulk-delete-alerts-in-vcf-operations-formerly-know-as-aria-operations/</link><pubDate>Tue, 17 Mar 2026 13:53:09 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/bulk-delete-alerts-in-vcf-operations-formerly-know-as-aria-operations/</guid><description>&lt;p&gt;If you manage a VMware Aria Operations (formerly vROps) environment, you know how quickly alert fatigue can set in. In this guide I will focus specifically on the &lt;strong&gt;&amp;ldquo;Guest OS Experiencing CPU queue&amp;rdquo;&lt;/strong&gt; alert. While helpful in identifying CPU-constrained VMs, it can quickly generate thousands of entries if you have a heavily over provisioned environment.&lt;/p&gt;
&lt;p&gt;Inspired by &lt;a href="https://www.brockpeterson.com/post/vmware-aria-operations-alert-hygiene"&gt;Brock Peterson’s excellent guide on Alert Hygiene&lt;/a&gt;, we are going to look at exactly how to cancel and delete these specific CPU queue alerts, both via the User Interface and programmatically via the API.&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you manage a VMware Aria Operations (formerly vROps) environment, you know how quickly alert fatigue can set in. In this guide I will focus specifically on the &lt;strong&gt;&amp;ldquo;Guest OS Experiencing CPU queue&amp;rdquo;&lt;/strong&gt; alert. While helpful in identifying CPU-constrained VMs, it can quickly generate thousands of entries if you have a heavily over provisioned environment.&lt;/p&gt;
&lt;p&gt;Inspired by &lt;a href="https://www.brockpeterson.com/post/vmware-aria-operations-alert-hygiene"&gt;Brock Peterson’s excellent guide on Alert Hygiene&lt;/a&gt;, we are going to look at exactly how to cancel and delete these specific CPU queue alerts, both via the User Interface and programmatically via the API.&lt;/p&gt;
&lt;h2 id="why-is-this-alert-so-noisy-the-false-positive-bug"&gt;Why is this Alert So Noisy? (The False Positive Bug)&lt;/h2&gt;
&lt;p&gt;According to Broadcom (KB 376641), Aria Operations versions 8.18.X and earlier suffer from a low sensitivity threshold for this specific alert.&lt;/p&gt;
&lt;p&gt;By default, the alert triggers if the Peak vCPU Queue exceeds a threshold of 10 and the overall CPU Usage goes beyond a mere 250 MHz. In modern environments, a VM can easily hit 250 MHz without actually experiencing any tangible performance degradation, resulting in massive alert spam. &lt;em&gt;(Note: Broadcom notes this underlying issue is resolved in VCF Operations 9.0).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Before we go nuclear and delete the existing alerts, let&amp;rsquo;s look at how to tune it properly.&lt;/p&gt;
&lt;h2 id="the-official-fix-tuning-the-alert-definition"&gt;The Official Fix: Tuning the Alert Definition&lt;/h2&gt;
&lt;p&gt;Instead of disabling the alert entirely, Broadcom recommends tuning the &lt;a href="https://cosmin.us/navigating-alerts-symptoms-and-notifications-in-vmware-aria-operations/"&gt;alert symptoms&lt;/a&gt; so it only triggers when there is &lt;em&gt;sizable&lt;/em&gt; CPU utilization.&lt;/p&gt;
&lt;p&gt;Broadcom provides an XML file in &lt;a href="https://knowledge.broadcom.com/external/article/376641/guest-os-experiencing-cpu-queue-false-po.html"&gt;KB 376641&lt;/a&gt; that replaces the &lt;code&gt;CPU Usage &amp;gt; 250 Mhz&lt;/code&gt; symptom with &lt;code&gt;CPU Net Run &amp;gt; 75%&lt;/code&gt;. It also increases the Wait Cycle to 10 minutes (to avoid alerting on quick CPU spikes) and drops the Cancel Cycle to 1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to apply the fix:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download the &lt;code&gt;Guest OS CPU Queue Alert.xml&lt;/code&gt; file from the &lt;a href="https://knowledge.broadcom.com/external/article/376641/guest-os-experiencing-cpu-queue-false-po.html"&gt;Broadcom KB article&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In Aria Operations, navigate to &lt;strong&gt;Configure&lt;/strong&gt; &amp;gt; &lt;strong&gt;Alerts&lt;/strong&gt; &amp;gt; &lt;strong&gt;Alert Definitions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;three dots (&amp;hellip;)&lt;/strong&gt; menu next to Add and select &lt;strong&gt;Import&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Browse and select the XML file you downloaded.&lt;/li&gt;
&lt;li&gt;Check the box for &lt;strong&gt;&amp;ldquo;Overwrite existing Alert Definition&amp;rdquo;&lt;/strong&gt; and click &lt;strong&gt;Import&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that the alert is fixed going forward, let&amp;rsquo;s clean up the thousands of old alerts sitting in your database.&lt;/p&gt;
&lt;p&gt;Here is your step-by-step guide to cleaning up the noise.&lt;/p&gt;
&lt;h2 id="method-1-clearing-the-alerts-via-the-ui"&gt;Method 1: Clearing the Alerts via the UI&lt;/h2&gt;
&lt;p&gt;If you just want to do a quick manual clean-up, the UI is the easiest path.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Log into your VMware Aria Operations console.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Navigate to the &lt;strong&gt;Troubleshoot&lt;/strong&gt; tab on the left-hand menu and select &lt;strong&gt;Alerts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; To isolate our noisy alerts, use the &lt;strong&gt;Group by&lt;/strong&gt; dropdown menu and select &lt;strong&gt;Alert Definition&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Scroll down or use the quick filter to find the &lt;strong&gt;Guest OS Experiencing CPU queue&lt;/strong&gt; grouping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; Select that specific grouping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; Click on the &lt;strong&gt;ACTIONS&lt;/strong&gt; dropdown menu at the top of the list and select &lt;strong&gt;Cancel Alert&lt;/strong&gt;. &lt;em&gt;Note: Once canceled, you will notice the lightbulb icon in the Status column turns gray, indicating the alerts are now Inactive.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/image-1024x575.webp" alt="Alerts tab in VMware Aria Operations, with options like “Cancel Alert,” “Delete Canceled Alerts,” and “Group By Definition." width="1024" height="575" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Step 7:&lt;/strong&gt; With the grouping still selected, click on the &lt;strong&gt;ACTIONS&lt;/strong&gt; menu one more time and select &lt;strong&gt;Delete Canceled Alert&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/image-1-1024x491.webp" alt="Menu with options like “Cancel Alert,” “Delete Canceled Alerts,” and “Suspend” under the “ACTIONS” dropdown in a VCF Operations interface." width="1024" height="491" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Just like that, all your historical CPU queue alerts are gone!&lt;/p&gt;
&lt;h2 id="method-2-clearing-the-alerts-via-the-api-programmatic-approach"&gt;Method 2: Clearing the Alerts via the API (Programmatic Approach)&lt;/h2&gt;
&lt;p&gt;If you have tens of thousands of alerts or want to build a scheduled script to handle this automatically, you can use the Aria Operations REST API. For ease of use I will use the swagger API. the swagger API can be found by going to the Operations URL https://vra_master/suite-api/doc/swagger-ui.html&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Identify the Alert UUIDs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;First, we need to find the active alerts to cancel them. You will use the &lt;code&gt;POST /api/alerts/query&lt;/code&gt; endpoint. The response will provide you with a list of alerts, each containing an &lt;code&gt;alertId&lt;/code&gt; string.&lt;/p&gt;
&lt;p&gt;As an example my JSON for Querry looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;activeOnly&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alertCriticality&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CRITICAL&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IMMEDIATE&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;WARNING&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;INFORMATION&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;Step 2: Cancel the Alerts&lt;/strong&gt; the Alerts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now that you have your list of &lt;code&gt;alertId&lt;/code&gt; values, you need to make them &lt;em&gt;Inactive&lt;/em&gt;. You will use the &lt;code&gt;POST /api/alerts&lt;/code&gt; endpoint. In your JSON payload, you will include the &lt;code&gt;alertId&lt;/code&gt; strings within the &lt;code&gt;uuids&lt;/code&gt; field and set the action to &lt;code&gt;cancel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Bulk Delete Inactive Alerts (Targeted vs. Global)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once the alerts are canceled, they sit in the database as &amp;ldquo;Inactive&amp;rdquo;. You have two ways to delete them depending on your goal:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option A: Targeted Delete (Safest):&lt;/strong&gt; If you &lt;em&gt;only&lt;/em&gt; want to delete the CPU queue alerts you just canceled, send a &lt;code&gt;DELETE&lt;/code&gt; request to the standard &lt;code&gt;/api/alerts&lt;/code&gt; endpoint and pass the exact same list of &lt;code&gt;alertId&lt;/code&gt; UUIDs you gathered in Step 1.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alert-query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alertStatus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alertStatus&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CANCELED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alertId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;alert-uuid&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Option B: The Global Purge:&lt;/strong&gt; If you want to use this opportunity to clean house and delete &lt;strong&gt;ALL&lt;/strong&gt; inactive alerts across your entire Aria Operations environment, simply run an empty &lt;code&gt;DELETE&lt;/code&gt; request against the &lt;code&gt;/api/alerts/bulk&lt;/code&gt; endpoint. &lt;em&gt;Warning: This will wipe out all canceled alerts, regardless of the alert type!&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alert-query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alertStatus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;alertStatus&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CANCELED&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Pro-Tip: Adjusting Global Retention&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t want to manually do this every week, you can instruct Aria Operations to clean up Inactive alerts faster.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Administration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Global Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Data Retention&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Look for the &lt;strong&gt;Symptoms/Alerts&lt;/strong&gt; setting.&lt;/li&gt;
&lt;li&gt;By default, Aria Operations keeps canceled/inactive alerts for 30 days. You can safely lower this (Brock recommends 10 days) to keep your database lean and your troubleshoot tabs clean.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/image-3-1024x394.webp" alt="In the VMware Aria Operations UI, navigate to Administration &amp;gt; Global Settings &amp;gt; Data Retention to adjust alert retention settings." width="1024" height="394" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonus: How to Stop the Alert from Triggering Altogether&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t care about &amp;ldquo;Guest OS Experiencing CPU queue&amp;rdquo; alerts &lt;em&gt;at all&lt;/em&gt; and want to stop them from coming back permanently, you should disable them in your active policy:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Configure&lt;/strong&gt; &amp;gt; &lt;strong&gt;Policies&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Edit your active/default policy.&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Alerts and Symptoms&lt;/strong&gt; tile.&lt;/li&gt;
&lt;li&gt;Search for &amp;ldquo;Guest OS Experiencing CPU queue&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Change the state to &lt;strong&gt;Deactivated&lt;/strong&gt; (represented by a crossed-out circle) and save the policy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://cosmin.us/images/2026/03/image-4-1024x332.webp" alt="Policy editing for alerts, with “Guest OS experiencing CPU queue” alert state set to “Deactivated” in a vCenter policy." width="1024" height="332" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;If host maintenance windows are a big source of your alert noise, also check out my guide on how to &lt;a href="https://cosmin.us/suppress-esxi-host-alerts-in-vcf-operations-9-when-a-host-enters-maintenance-mode/"&gt;suppress ESXi host alerts in VCF Operations 9 when a host enters maintenance mode&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>Unstuck Your VCF Lab: A Script to Bypass Compatibility &amp; Interoperability Checks</title><link>https://cosmin.us/unstuck-your-vcf-lab-a-script-to-bypass-compatibility-interoperability-checks/</link><pubDate>Mon, 09 Feb 2026 02:58:43 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/unstuck-your-vcf-lab-a-script-to-bypass-compatibility-interoperability-checks/</guid><description>&lt;p&gt;If you spend enough time building out VMware Cloud Foundation (VCF) in a lab, Proof of Concept, or nested environment, you will eventually hit &amp;ldquo;The Wall.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;You know the hardware works. You know the ESXi versions will play nicely together. But SDDC Manager’s automated guardrails, the strict Hardware Compatibility List (HCL) checks and VVS (VMware Validated Solutions) interoperability validations throw up a red flag and stop your deployment or upgrade dead in its tracks.&lt;/p&gt;</description><content:encoded>&lt;p&gt;If you spend enough time building out VMware Cloud Foundation (VCF) in a lab, Proof of Concept, or nested environment, you will eventually hit &amp;ldquo;The Wall.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;You know the hardware works. You know the ESXi versions will play nicely together. But SDDC Manager’s automated guardrails, the strict Hardware Compatibility List (HCL) checks and VVS (VMware Validated Solutions) interoperability validations throw up a red flag and stop your deployment or upgrade dead in its tracks.&lt;/p&gt;
&lt;p&gt;VCF is designed to be rigid for stability in production. But in a lab, sometimes you just need to force things through to test a specific feature or scenario.&lt;/p&gt;
&lt;p&gt;Today, I’m sharing a quick shell script that modifies the LCM and Domain Manager configurations on the SDDC Manager appliance to bypass these strict validation checks.&lt;/p&gt;
&lt;h3 id="the-big-disclaimer"&gt;The Big Disclaimer&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;STOP. READ THIS FIRST.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This procedure is &lt;strong&gt;not supported by VMware GSS&lt;/strong&gt; for production environments. These guardrails exist to prevent you from building an unstable or unsupportable environment.&lt;/p&gt;
&lt;p&gt;By running this script, you are telling SDDC Manager to ignore safety checks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Do not&lt;/strong&gt; run this in production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only&lt;/strong&gt; use this in labs, POCs, or nested environments where you accept the risk of instability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always&lt;/strong&gt; take a snapshot of your SDDC Manager VM before proceeding.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="what-does-the-script-do"&gt;What Does The Script Do?&lt;/h3&gt;
&lt;p&gt;The script targets two main guardrails within the SDDC Manager appliance:&lt;/p&gt;
&lt;h4 id="1-the-lcm-compatibility-check"&gt;1. The LCM Compatibility Check&lt;/h4&gt;
&lt;p&gt;The Lifecycle Manager (LCM) consults the VMware Compatibility Guide to ensure your hardware (servers, I/O cards, drives) is validated for the target VCF version.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The config modified:&lt;/strong&gt; &lt;code&gt;/opt/vmware/vcf/lcm/lcm-app/conf/compatibility.flag&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The action:&lt;/strong&gt; By adding the flag &lt;code&gt;vcf.compatibility.controllers.compatibilityCheckEnabled=false&lt;/code&gt;, we tell LCM to skip this HCL lookup.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="2-the-domain-manager-vvs-validation"&gt;2. The Domain Manager VVS Validation&lt;/h4&gt;
&lt;p&gt;The Domain Manager handles the creation and expansion of Workload Domains. It has internal checks to ensure your configuration adheres to VMware Validated Solutions (VVS) interoperability standards.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The config modified:&lt;/strong&gt; &lt;code&gt;/etc/vmware/vcf/domainmanager/application-prod.properties&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The action:&lt;/strong&gt; By adding &lt;code&gt;vcf.domainmanager.validation.enableVvsInteropValidation=false&lt;/code&gt;, we loosen constraints related to interop validation during domain operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="the-quick--dirty-commands"&gt;The &amp;ldquo;Quick &amp;amp; Dirty&amp;rdquo; Commands&lt;/h3&gt;
&lt;p&gt;If you just want to copy-paste the raw commands right into your SSH session as root, here is what you need to do. It appends the necessary flags to the config files and restarts the relevant services to apply the changes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vcf.compatibility.controllers.compatibilityCheckEnabled=false&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /opt/vmware/vcf/lcm/lcm-app/conf/compatibility.flag
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl restart lcm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vcf.domainmanager.validation.enableVvsInteropValidation=false&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/vmware/vcf/domainmanager/application-prod.properties
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl restart domainmanager.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="the-better-way-reusable-script"&gt;The &amp;ldquo;Better&amp;rdquo; Way (Reusable Script)&lt;/h3&gt;
&lt;p&gt;If you are frequently tearing down and rebuilding labs, it’s better to wrap this into a reusable script with a few basic safety checks (like ensuring you are root and creating backups of the config files before modifying them).&lt;/p&gt;
&lt;p&gt;Here is a version of the process script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Title: disable-vcf-validations.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Description: Disables LCM compatibility check and Domain Manager VVS interop validation on SDDC Manager.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# NOTE: FOR LAB USE ONLY. NOT SUPPORTED IN PRODUCTION.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. Ensure running as Root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$EUID&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -ne &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Please run as root&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--- Starting VCF Validation Bypass ---&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. Disable LCM Compatibility Check&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LCM_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/vmware/vcf/lcm/lcm-app/conf/compatibility.flag&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$LCM_CONF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Backing up LCM config...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cp &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$LCM_CONF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LCM_CONF&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.bak&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Disabling LCM Compatibility Check flag...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vcf.compatibility.controllers.compatibilityCheckEnabled=false&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$LCM_CONF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Restarting LCM service...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; systemctl restart lcm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Error: LCM config file not found at &lt;/span&gt;&lt;span class="nv"&gt;$LCM_CONF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;------------------------------------&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. Disable Domain Manager VVS Validation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DM_PROPS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/etc/vmware/vcf/domainmanager/application-prod.properties&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DM_PROPS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Backing up Domain Manager properties...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cp &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DM_PROPS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DM_PROPS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.bak&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Disabling VVS Interop Validation flag...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vcf.domainmanager.validation.enableVvsInteropValidation=false&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DM_PROPS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Restarting Domain Manager service...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; systemctl restart domainmanager.service
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Error: Domain Manager properties not found at &lt;/span&gt;&lt;span class="nv"&gt;$DM_PROPS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--- Finished. Validations disabled. ---&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="how-to-use-it"&gt;How to use it:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;SSH into your SDDC Manager appliance using the &lt;code&gt;vcf&lt;/code&gt; user and su to &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a file named &lt;code&gt;disable-vcf-validations.sh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Paste the script code above into the file.&lt;/li&gt;
&lt;li&gt;Make it executable: &lt;code&gt;chmod +x disable-vcf-validations.sh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run it: &lt;code&gt;./disable-vcf-validations.sh&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once the services restart, retry the operation that was previously blocked in the SDDC Manager UI. If the failed workflow is still sitting there in a running state, you may first need to &lt;a href="https://cosmin.us/stopping-a-running-task-in-sddc-manager-a-step-by-step-guide/"&gt;stop the running task in SDDC Manager&lt;/a&gt;. Happy labbing!&lt;/p&gt;</content:encoded></item><item><title>Salt vs. BigFix: The Speed of Light vs. The Heavyweight Champion</title><link>https://cosmin.us/salt-vs-bigfix-the-speed-of-light-vs-the-heavyweight-champion/</link><pubDate>Tue, 27 Jan 2026 18:40:05 +0000</pubDate><dc:creator>Cosmin Trif</dc:creator><guid>https://cosmin.us/salt-vs-bigfix-the-speed-of-light-vs-the-heavyweight-champion/</guid><description>&lt;p&gt;In the world of infrastructure management, we are often forced to choose between the &lt;strong&gt;legacy reliability of the past&lt;/strong&gt; and the &lt;strong&gt;high-speed automation of the future&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For years, &lt;strong&gt;HCL BigFix&lt;/strong&gt; (formerly IBM BigFix) has been the &amp;ldquo;Heavyweight Champion&amp;rdquo;—a massive, unmovable force in endpoint management. But a challenger has matured into a dominant player: &lt;strong&gt;Salt (SaltStack)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you are building a modern, self-healing infrastructure, the conversation shouldn&amp;rsquo;t just be about &amp;ldquo;patching.&amp;rdquo; It should be about &lt;strong&gt;reaction time&lt;/strong&gt;, &lt;strong&gt;extensibility&lt;/strong&gt;, and &lt;strong&gt;developer experience&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded>&lt;p&gt;In the world of infrastructure management, we are often forced to choose between the &lt;strong&gt;legacy reliability of the past&lt;/strong&gt; and the &lt;strong&gt;high-speed automation of the future&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For years, &lt;strong&gt;HCL BigFix&lt;/strong&gt; (formerly IBM BigFix) has been the &amp;ldquo;Heavyweight Champion&amp;rdquo;—a massive, unmovable force in endpoint management. But a challenger has matured into a dominant player: &lt;strong&gt;Salt (SaltStack)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you are building a modern, self-healing infrastructure, the conversation shouldn&amp;rsquo;t just be about &amp;ldquo;patching.&amp;rdquo; It should be about &lt;strong&gt;reaction time&lt;/strong&gt;, &lt;strong&gt;extensibility&lt;/strong&gt;, and &lt;strong&gt;developer experience&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here is why Salt doesn&amp;rsquo;t just compete with BigFix—it changes the game entirely.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-old-guard-where-bigfix-fits"&gt;The Old Guard: Where BigFix Fits&lt;/h2&gt;
&lt;p&gt;Let’s be fair: BigFix is a tank. It is legendary for its ability to manage hundreds of thousands of endpoints, even over slow satellite links. Its &amp;ldquo;Relevance Language&amp;rdquo; is incredibly efficient at querying endpoint status without draining CPU.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But BigFix shows its age in the modern DevOps world:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Learning Curve:&lt;/strong&gt; &amp;ldquo;Relevance Language&amp;rdquo; is proprietary and complex. It is a skill you learn &lt;em&gt;only&lt;/em&gt; for BigFix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Paradigm:&lt;/strong&gt; BigFix is primarily a &lt;em&gt;polling-based&lt;/em&gt; system. It asks, &amp;ldquo;Are you compliant?&amp;rdquo; and waits for an answer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Closed Box:&lt;/strong&gt; It is a commercial product with a &amp;ldquo;black box&amp;rdquo; feel compared to open-source ecosystems.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="why-salt-stands-out-the-react-vs-poll-revolution"&gt;Why Salt Stands Out: The &amp;ldquo;React vs. Poll&amp;rdquo; Revolution&lt;/h2&gt;
&lt;p&gt;Salt was built differently. It wasn&amp;rsquo;t designed just to &lt;em&gt;manage&lt;/em&gt; servers; it was designed to &lt;em&gt;control&lt;/em&gt; them at the speed of thought.&lt;/p&gt;
&lt;h3 id="1-the-event-bus-the-killer-feature"&gt;1. The Event Bus (The Killer Feature)&lt;/h3&gt;
&lt;p&gt;This is where Salt leaves BigFix in the dust. BigFix relies on agents checking in. Salt relies on a &lt;strong&gt;bi-directional Event Bus&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BigFix&lt;/strong&gt; says: &lt;em&gt;&amp;ldquo;I will check this server every few minutes to see if the service is down.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Salt&lt;/strong&gt; says: &lt;em&gt;&amp;ldquo;The service just died. The minion fired an event to the master. The master triggered a Reactor state. The service is already restarting.&amp;rdquo;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows for &lt;strong&gt;Event-Driven Automation&lt;/strong&gt;. You aren&amp;rsquo;t just &lt;a href="https://cosmin.us/saltstack-the-ultimate-tool-for-windows-patch-management/"&gt;patching&lt;/a&gt;; you are building a &lt;strong&gt;self-healing infrastructure&lt;/strong&gt; that reacts to security threats, file changes, or load spikes in milliseconds.&lt;/p&gt;
&lt;h3 id="2-speed-zeromq"&gt;2. Speed (ZeroMQ)&lt;/h3&gt;
&lt;p&gt;Salt is built on &lt;strong&gt;ZeroMQ&lt;/strong&gt;, a high-performance asynchronous messaging library. It is capable of parallel execution across thousands of nodes nearly instantly.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Remote Execution:&lt;/strong&gt; Want to check disk usage on 10,000 servers? With Salt, you get the results back in seconds, not minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &amp;ldquo;Feel&amp;rdquo;:&lt;/strong&gt; Using the Salt CLI feels like you are typing on the local machine, even when you are controlling a fleet of thousands.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-python-vs-relevance"&gt;3. Python vs. Relevance&lt;/h3&gt;
&lt;p&gt;BigFix requires you to learn Relevance Language (which looks like Q: name of operating system).&lt;/p&gt;
&lt;p&gt;Salt is built on Python.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extensibility:&lt;/strong&gt; If Salt can&amp;rsquo;t do something out of the box, you can write a standard Python module to do it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hiring:&lt;/strong&gt; It is infinitely easier to find an engineer who knows Python than one who is a certified BigFix Relevance expert.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="the-code-face-off-auto-healing-a-service"&gt;The Code Face-Off: Auto-Healing a Service&lt;/h2&gt;
&lt;p&gt;Let’s look at a real-world scenario: &lt;strong&gt;Nginx crashes on a server.&lt;/strong&gt; How do these two tools handle it?&lt;/p&gt;
&lt;h3 id="the-bigfix-method-the-loop"&gt;The BigFix Method: &amp;ldquo;The Loop&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;BigFix relies on a Fixlet that contains &lt;strong&gt;Relevance&lt;/strong&gt; (the condition) and &lt;strong&gt;Action Script&lt;/strong&gt; (the fix). The client evaluates this loop periodically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Relevance (Condition):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;exists service &amp;#34;nginx&amp;#34; whose (state of it = &amp;#34;Stopped&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Action Script (The Fix):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;waithidden /usr/sbin/service nginx start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Pro:&lt;/strong&gt; It works reliably even if the network is flaky.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Con:&lt;/strong&gt; &lt;strong&gt;Latency.&lt;/strong&gt; You are at the mercy of the client&amp;rsquo;s polling interval. If your loop is set to 15 minutes, your web server is down for 15 minutes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="the-salt-method-the-reactor"&gt;The Salt Method: &amp;ldquo;The Reactor&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;Salt pushes intelligence to the edge. The Minion monitors the service and fires an event the &lt;em&gt;moment&lt;/em&gt; it dies. The Master catches that event and fires a reaction immediately.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Beacon (On the Minion):&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Configured to watch the service and fire an event only when it changes.&lt;/p&gt;
&lt;p&gt;YAML&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;beacons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;onchangeonly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;The Reactor (On the Master):&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Listens for the event and triggers the fix instantly.&lt;/p&gt;
&lt;p&gt;YAML&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;restart_nginx:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; local.service.start:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - tgt: {{ data[&amp;#39;id&amp;#39;] }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - arg:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - nginx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Result:&lt;/strong&gt; The moment Nginx crashes, Salt restarts it. The downtime is measured in &lt;strong&gt;milliseconds&lt;/strong&gt;, not minutes.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="the-head-to-head"&gt;The Head-to-Head&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;BigFix&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;Salt (SaltStack)&lt;/strong&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Primary Architecture&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Relay-based (Polling/UDP)&lt;/td&gt;
					&lt;td&gt;Master/Minion (ZeroMQ Event Bus)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Relevance &amp;amp; Action Script (Proprietary)&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Python&lt;/strong&gt; &amp;amp; YAML (Universal)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Reaction Time&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Minutes (Polling Interval)&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Milliseconds&lt;/strong&gt; (Event-Driven)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Automation Style&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Compliance Enforcement&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Reactive &amp;amp; Orchestrated&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Community&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Vendor-locked&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Open Source&lt;/strong&gt; (Vibrant ecosystem)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="the-verdict"&gt;The Verdict:&lt;/h2&gt;
&lt;p&gt;BigFix is an excellent tool for &amp;ldquo;Endpoint Management&amp;rdquo;—keeping a laptop fleet patched and compliant.&lt;/p&gt;
&lt;p&gt;But if you are running &lt;strong&gt;servers, cloud infrastructure, or applications&lt;/strong&gt;, you need more than management. You need &lt;strong&gt;Orchestration&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Salt stands out because it bridges the gap between &lt;em&gt;configuration management&lt;/em&gt; (like &lt;a href="https://cosmin.us/saltstack-vs-ansible-which-open-source-configuration-management-tool-is-best-for-your-needs/"&gt;Ansible&lt;/a&gt;) and &lt;em&gt;remote execution&lt;/em&gt;. It allows you to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Define your infrastructure as code&lt;/strong&gt; (State files).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React to problems instantly&lt;/strong&gt; (Beacons/Reactors).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scale without losing speed&lt;/strong&gt; (ZeroMQ).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the modern engineer, the choice is clear. Stop polling your infrastructure and asking it how it&amp;rsquo;s doing. &lt;strong&gt;Start listening to it with Salt.&lt;/strong&gt; If you want to try it yourself, start with my &lt;a href="https://cosmin.us/step-by-step-guide-to-deploying-salt-open-source/"&gt;step-by-step guide to deploying Salt open source&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;</content:encoded></item></channel></rss>