<?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/">
  <channel>
    <title>NSX on The Nested Lab</title>
    <link>https://thenestedlab.com/products/nsx/</link>
    <description>Recent content in NSX on The Nested Lab</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Thu, 01 Oct 2026 00:00:00 +0100</lastBuildDate>
    <atom:link href="https://thenestedlab.com/products/nsx/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Every resource in the VCF Automation 9.1 blueprint designer: the complete guide</title>
      <link>https://thenestedlab.com/posts/vcfa-blueprint-resource-reference/</link>
      <pubDate>Thu, 01 Oct 2026 00:00:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/vcfa-blueprint-resource-reference/</guid>
      <description>A complete guide to the VCF Automation 9.1 blueprint designer: every palette item, the YAML behind it, every field the platform accepts, recipes and traps. Every snippet validated, dry-run or deployed.</description>
      <content:encoded><![CDATA[<p>The blueprint designer in a VCF Automation 9.1 All Apps organization offers
sixteen items in three groups. Drag one onto the canvas and you get a few
lines of YAML. What you may write underneath them is spread across the VM
Service, VKS, NSX VPC and Automation guides. For some items, it&rsquo;s written
down nowhere at all.</p>
<p>This guide puts it in one place. For each item, you get:</p>
<ul>
<li>what it creates, and the YAML behind it;</li>
<li>every field the platform accepts;</li>
<li>a minimal snippet, and recipes for the common jobs;</li>
<li>the status fields worth reading;</li>
<li>the traps we hit.</li>
</ul>
<p>Three things make it more than a list from memory, which, given my memory,
is just as well:</p>
<ul>
<li><strong>The field lists come from the platform.</strong> VCF Automation publishes the
schema of every blueprint resource type through its API. The designer
carries the schema of every palette item. The Supervisor publishes the
definition of every Kubernetes kind it serves, and VCF Automation&rsquo;s VPC API
publishes its own. Where they disagree (and they do), the tables follow
what the platform enforces.</li>
<li><strong>Every snippet was checked.</strong> Each one went through VCF Automation&rsquo;s
validation API, and every Kubernetes manifest through a server-side dry
run on the Supervisor. Five test blueprints (all of them in the downloads)
deployed every item for real. The remaining recipes are ones our lab
catalog deploys every day.</li>
<li><strong>The traps are real.</strong> Several of the most useful lines in this guide come
from things that went wrong while testing: a NAT rule that ignored its
port, a firewall rule that grew an &ldquo;Any&rdquo;, a request that waits for an
address that can never come.</li>
</ul>
<p>We checked it on our lab platform, f06:</p>
<ul>
<li>VCF Automation 9.1.0;</li>
<li>a Supervisor on Kubernetes 1.32.9, with the VM Operator API at <code>v1alpha5</code>;</li>
<li>VKS with ClusterClasses up to <code>builtin-generic-v3.6.0</code>, and Kubernetes
releases up to 1.35.5;</li>
<li>NSX VPCs.</li>
</ul>
<p>Names in the examples (<code>f06</code>, <code>nested-pod</code>, <code>vpc-student05</code>,
<code>vsan-default-storage-policy</code>) are ours; use yours.</p>
<p>In the field tables, <strong>Values</strong> gives a field&rsquo;s choices and default. Where the
schema has neither, it gives an example, marked <em>e.g.</em>: the value from our
tested blueprints wherever one of them set the field, otherwise a typical one.
For an object or a list, the example is a short YAML flow value built from its
fields (<code>...</code> marks the ones left out). And <code>&lt;...&gt;</code> stands for a name of yours.</p>
<h2 id="the-shape-of-a-blueprint">The shape of a blueprint</h2>
<p>An All Apps blueprint is YAML with <code>formatVersion: 2</code>, its inputs, its
resources and its outputs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">formatVersion</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">inputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vmName</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l">VM name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">default</span><span class="p">:</span><span class="w"> </span><span class="l">web-01</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">pattern</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;^[a-z0-9]([-a-z0-9]*[a-z0-9])?$&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">size</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l">Size</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">default</span><span class="p">:</span><span class="w"> </span><span class="l">small</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">oneOf</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="nt">Small, const</span><span class="p">:</span><span class="w"> </span><span class="l">small}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="nt">Medium, const</span><span class="p">:</span><span class="w"> </span><span class="l">medium}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">team-a-dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">existing</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vm1</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vmoperator.vmware.com/v1alpha5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">${input.vmName}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">className</span><span class="p">:</span><span class="w"> </span><span class="l">${&#39;best-effort-&#39; + input.size}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">imageName</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-24.04-server-cloudimg-amd64</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">storageClass</span><span class="p">:</span><span class="w"> </span><span class="l">vsan-default-storage-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">outputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vmName</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vm1.object.metadata.name}</span><span class="w">
</span></span></span></code></pre></div><p>What the expressions can read:</p>
<table>
	<thead>
			<tr>
					<th>Expression</th>
					<th>Value</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>${input.&lt;name&gt;}</code></td>
					<td>A request input. An input group from a property group is <code>${input.&lt;group&gt;.&lt;property&gt;}</code>.</td>
			</tr>
			<tr>
					<td><code>${resource.&lt;name&gt;.&lt;property&gt;}</code></td>
					<td>Another resource&rsquo;s property; this also orders the two. <code>object</code> holds the live Kubernetes object of a Supervisor Resource.</td>
			</tr>
			<tr>
					<td><code>${propgroup.&lt;group&gt;.&lt;property&gt;}</code></td>
					<td>A constant property group&rsquo;s value.</td>
			</tr>
			<tr>
					<td><code>${secret.&lt;name&gt;}</code></td>
					<td>A VCF Automation secret, from the organization or the project.</td>
			</tr>
			<tr>
					<td><code>${env.deploymentName}</code>, <code>${count.index}</code></td>
					<td>The deployment&rsquo;s name; the instance number in a counted resource.</td>
			</tr>
			<tr>
					<td><code>${to_k8s_name(env.deploymentName, 63)}</code></td>
					<td>A string made safe for a Kubernetes name, as Broadcom&rsquo;s own samples use for <code>generateName</code>.</td>
			</tr>
	</tbody>
</table>
<p>Besides <code>type</code> and <code>properties</code>, each resource has <code>dependsOn</code> (an explicit
order) and <code>allocatePerInstance</code> (see <code>count</code> below). <code>formatVersion: 2</code> also
allows <code>metadata</code>, <code>variables</code>, and an output named <code>__deploymentOverview</code>,
whose Markdown value becomes the deployment&rsquo;s overview page.</p>
<h2 id="how-the-palette-maps-to-yaml">How the palette maps to YAML</h2>
<p>Behind the sixteen items there are only five resource types, and one of them
isn&rsquo;t in the palette. Most items are a type with part of its YAML already
filled in. For the workload items, that&rsquo;s a Kubernetes <code>apiVersion</code> and
<code>kind</code>; for the VPC items, it&rsquo;s a VPC configuration <code>kind</code>.</p>
<table>
	<thead>
			<tr>
					<th>Palette item</th>
					<th>YAML <code>type</code></th>
					<th>Pre-filled</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Supervisor Namespace</td>
					<td><code>CCI.Supervisor.Namespace</code></td>
					<td></td>
			</tr>
			<tr>
					<td><strong>VPC group</strong></td>
					<td></td>
					<td></td>
			</tr>
			<tr>
					<td>VPC</td>
					<td><code>CCI.VPC</code></td>
					<td></td>
			</tr>
			<tr>
					<td>VPC Configuration</td>
					<td><code>CCI.VPC.Configuration</code></td>
					<td>nothing: you set <code>kind</code></td>
			</tr>
			<tr>
					<td>Attachment</td>
					<td><code>CCI.VPC.Configuration</code></td>
					<td><code>kind: VPCAttachment</code></td>
			</tr>
			<tr>
					<td>IP Address Allocation</td>
					<td><code>CCI.VPC.Configuration</code></td>
					<td><code>kind: VPCIPAddressAllocation</code></td>
			</tr>
			<tr>
					<td>NAT Rule</td>
					<td><code>CCI.VPC.Configuration</code></td>
					<td><code>kind: VPCNATRule</code></td>
			</tr>
			<tr>
					<td>Group</td>
					<td><code>CCI.VPC.Configuration</code></td>
					<td><code>kind: VPCNetworkSecurityGroup</code></td>
			</tr>
			<tr>
					<td>Gateway Firewall Policy</td>
					<td><code>CCI.VPC.Configuration</code></td>
					<td><code>kind: VPCGatewayFirewallPolicy</code></td>
			</tr>
			<tr>
					<td><strong>Workload group</strong></td>
					<td></td>
					<td></td>
			</tr>
			<tr>
					<td>Supervisor Resource</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td>nothing: any manifest</td>
			</tr>
			<tr>
					<td>Virtual Machine</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>vmoperator.vmware.com/v1alpha5</code> <code>VirtualMachine</code></td>
			</tr>
			<tr>
					<td>Virtual Machine Group</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>vmoperator.vmware.com/v1alpha5</code> <code>VirtualMachineGroup</code></td>
			</tr>
			<tr>
					<td>Virtual Machine Service</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>vmoperator.vmware.com/v1alpha3</code> <code>VirtualMachineService</code></td>
			</tr>
			<tr>
					<td>Subnet</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>crd.nsx.vmware.com/v1alpha1</code> <code>Subnet</code></td>
			</tr>
			<tr>
					<td>Persistent Volume Claim</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>v1</code> <code>PersistentVolumeClaim</code></td>
			</tr>
			<tr>
					<td>Secret</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>v1</code> <code>Secret</code></td>
			</tr>
			<tr>
					<td>Kubernetes Cluster</td>
					<td><code>CCI.Supervisor.Resource</code></td>
					<td><code>cluster.x-k8s.io/v1beta1</code> <code>Cluster</code></td>
			</tr>
			<tr>
					<td><em>(not in the palette)</em></td>
					<td><code>Util.PasswordEntry</code></td>
					<td></td>
			</tr>
	</tbody>
</table>
<p>Four consequences, worth knowing before any of the detail:</p>
<ul>
<li><strong>Anything the Supervisor understands can go in a blueprint.</strong> The workload
items are shortcuts. The generic Supervisor Resource takes any manifest the
namespace accepts. Our lab blueprints rely on a kind the palette doesn&rsquo;t
offer, <code>SubnetConnectionBindingMap</code>, to carry VLANs.</li>
<li><strong>The VPC items are a closed list.</strong> <code>CCI.VPC.Configuration</code> takes exactly
the five kinds above. A VPC&rsquo;s load balancer is a sixth kind in VCF
Automation&rsquo;s VPC API, and a blueprint can&rsquo;t make one (see
<a href="#vpc">VPC</a>).</li>
<li><strong>VCF Automation validates the outside, the platform the inside.</strong> The
validation API checks the resource type&rsquo;s own properties: required fields,
patterns, the namespace&rsquo;s two shapes. It doesn&rsquo;t look inside a <code>manifest</code>
or a <code>configs[].spec</code>: in our test, <code>powerState: Sideways</code> passed
validation. Those are checked when the request runs.</li>
<li><strong>The designer&rsquo;s schemas are not the platform&rsquo;s.</strong> The palette&rsquo;s forms
come from schemas bundled with VCF Automation, while the Supervisor and
the VPC API check against their own. They disagree in a handful of
places, listed next.</li>
</ul>
<h2 id="where-the-designer-and-the-platform-disagree">Where the designer and the platform disagree</h2>
<p>We compared each palette item&rsquo;s schema with the platform&rsquo;s definition of the
same <code>apiVersion</code> and <code>kind</code>. We went field by field (every bit as gripping
as it sounds) and tested every difference:</p>
<table>
	<thead>
			<tr>
					<th>Item</th>
					<th>The designer offers</th>
					<th>What the platform does</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Virtual Machine</td>
					<td><code>affinity.zoneAffinity</code>, <code>affinity.zoneAntiAffinity</code>, and VM affinity terms named <code>...IgnoredDuringExecution</code></td>
					<td>Rejects them as unknown fields. VM affinity and anti-affinity take <code>requiredDuringSchedulingPreferredDuringExecution</code> and <code>preferredDuringSchedulingPreferredDuringExecution</code>.</td>
			</tr>
			<tr>
					<td>Virtual Machine</td>
					<td>no <code>linuxPrep.password</code>, <code>linuxPrep.scriptText</code>, <code>crypto.vTPMMode</code>, <code>currentSnapshotName</code>, or disk options at volume level</td>
					<td>Accepts all of them.</td>
			</tr>
			<tr>
					<td>Virtual Machine</td>
					<td><code>bootOptions.firmware</code> as <code>BIOS</code> or <code>EFI</code>, and <code>bootOptions.enterBootSetup</code></td>
					<td><code>spec.bootOptions.firmware: Unsupported value: &quot;BIOS&quot;: supported values: &quot;bios&quot;, &quot;efi&quot;</code>, and <code>strict decoding error: unknown field &quot;spec.bootOptions.enterBootSetup&quot;</code>.</td>
			</tr>
			<tr>
					<td>Virtual Machine <code>v1alpha4</code>, <code>v1alpha3</code></td>
					<td><code>network.nameServers</code></td>
					<td>The field is <code>nameservers</code>, lower case.</td>
			</tr>
			<tr>
					<td>Subnet</td>
					<td><code>regionName</code>, <code>ipBlockNames</code>, <code>description</code></td>
					<td>Rejects them. Accepts <code>vlanConnectionName</code>, which the designer doesn&rsquo;t show.</td>
			</tr>
			<tr>
					<td>Persistent Volume Claim</td>
					<td><code>accessMode</code></td>
					<td><code>strict decoding error: unknown field &quot;spec.accessMode&quot;</code>. The field is <code>accessModes</code>.</td>
			</tr>
			<tr>
					<td>Kubernetes Cluster</td>
					<td><code>cluster.x-k8s.io/v1beta1</code></td>
					<td>Serves it, with <code>cluster.x-k8s.io/v1beta1 Cluster is deprecated; use cluster.x-k8s.io/v1beta2 Cluster</code>.</td>
			</tr>
			<tr>
					<td>Group</td>
					<td><code>vmSelectors[].selector</code>, <code>podSelectors[].selector</code></td>
					<td><code>spec.vmSelectors[0]: Required value: must specify at least one selector</code>. The field is <code>labelSelector</code>; the API also offers <code>propertySelector</code>.</td>
			</tr>
			<tr>
					<td>Gateway Firewall Policy</td>
					<td><code>ruleCount</code></td>
					<td>Computed by the API; not accepted. A rule&rsquo;s <code>from</code> is required, though the schema says empty means any.</td>
			</tr>
	</tbody>
</table>
<p>None of this stops the designer from saving a blueprint. It surfaces when the
request runs.</p>
<h2 id="what-every-resource-shares">What every resource shares</h2>
<h3 id="properties-on-every-type">Properties on every type</h3>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>On</th>
					<th>What it does</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>count</code></td>
					<td>all</td>
					<td>How many instances to create. Default 1.</td>
			</tr>
			<tr>
					<td><code>allocatePerInstance</code></td>
					<td>all (beside <code>type</code>)</td>
					<td>Required for <code>${count.index}</code>: without it the validator refuses the blueprint with <code>should have allocatePerInstance property set to True</code>.</td>
			</tr>
			<tr>
					<td><code>dependsOn</code></td>
					<td>all (beside <code>type</code>)</td>
					<td>Explicit order. A reference to another resource orders the two already.</td>
			</tr>
			<tr>
					<td><code>context</code></td>
					<td>Supervisor Resource items</td>
					<td><strong>Required.</strong> The namespace the manifest goes into: <code>${resource.&lt;namespace&gt;.id}</code>.</td>
			</tr>
			<tr>
					<td><code>existing</code></td>
					<td>Namespace, Supervisor Resource items</td>
					<td><code>true</code> adopts what already exists instead of creating it.</td>
			</tr>
			<tr>
					<td><code>wait</code></td>
					<td>Supervisor Resource items, VPC Configuration</td>
					<td>When the resource counts as done.</td>
			</tr>
	</tbody>
</table>
<p>Recipe, two Secrets from one resource:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">allocatePerInstance</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">count</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.ns.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">${&#39;ref-s-&#39; + count.index}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Opaque</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">stringData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">note</span><span class="p">:</span><span class="w"> </span><span class="l">created by instance ${count.index}</span><span class="w">
</span></span></span></code></pre></div><p>The deployment then holds <code>sec[0]</code> and <code>sec[1]</code>, and the namespace
<code>ref-s-0</code> and <code>ref-s-1</code>.</p>
<h3 id="wait-when-a-resource-is-finished"><code>wait</code>: when a resource is finished</h3>
<p>Without a <code>wait</code>, a Supervisor Resource is finished as soon as the Supervisor
accepts the manifest. That&rsquo;s fine for a Secret, and wrong for a VM whose
address an output needs. <code>wait</code> takes conditions, fields, or both:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span><span class="nt">wait</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">conditions</span><span class="p">:</span><span class="w">                      </span><span class="c"># status.conditions[] of the object</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Ready</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;True&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Ready</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;False&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">reason</span><span class="p">:</span><span class="w"> </span><span class="l">Failed</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">indicatesFailure</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">       </span><span class="c"># this one fails the resource instead of finishing it</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">fields</span><span class="p">:</span><span class="w">                          </span><span class="c"># any field of the object, by path</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">status.powerState</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">PoweredOn</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">skipWaitOnDelete</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">          </span><span class="c"># true: deletion does not wait for the object to be gone</span><span class="w">
</span></span></span></code></pre></div><p>A Supervisor Resource can also watch log output with <code>executionLogs</code>:
<code>progressMessagePattern</code> while a matching message appears, and
<code>failureMessagePattern</code> to fail the resource.</p>
<p>The designer pre-fills a <code>wait</code> for some items:</p>
<table>
	<thead>
			<tr>
					<th>Item</th>
					<th>Designer&rsquo;s default <code>wait</code></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Virtual Machine</td>
					<td>condition <code>VirtualMachineCreated</code> = <code>True</code></td>
			</tr>
			<tr>
					<td>Virtual Machine Group</td>
					<td>condition <code>Ready</code> = <code>True</code></td>
			</tr>
			<tr>
					<td>NAT Rule, Group</td>
					<td>condition <code>Realized</code> = <code>True</code></td>
			</tr>
			<tr>
					<td>everything else</td>
					<td>none</td>
			</tr>
	</tbody>
</table>
<p>The VM&rsquo;s default came from a 9.0 problem: VM status could come back empty,
and Broadcom&rsquo;s KB 435137 gave this <code>wait</code> as the workaround. That condition
is true before the VM is even on, let alone has an address.</p>
<p>VCF Automation also waits by itself in one place: <strong>a Virtual Machine Service
of type <code>LoadBalancer</code> is not finished until it has an external address</strong>,
with or without a <code>wait</code>. In a VPC without a load balancer, that address
never comes, and the request stays in progress until it times out. Ours was
still <code>PARTIAL</code> after ten minutes, with the service <code>&lt;pending&gt;</code> on the
Supervisor.</p>
<h3 id="reading-a-resources-live-state">Reading a resource&rsquo;s live state</h3>
<p>Every Supervisor Resource exposes the object as the Supervisor sees it under
<code>object</code>, and a VPC Configuration exposes its objects under <code>configs</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">outputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vmIp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vm1.object.status.network.primaryIP4}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vmHost</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vm1.object.status.nodeName}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">lbIp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.webLb.object.status.loadBalancer.ingress[0].ip}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">publicIp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.publicIp.configs[0].spec.allocationIPs}</span><span class="w">
</span></span></span></code></pre></div><p><strong>Outputs are computed once, when the request finishes</strong>, and not refreshed
afterwards. A VM that waited only for <code>PoweredOn</code> finished before its guest
reported an address, and its IP output stayed empty for good. Waiting on the
condition that marks the guest&rsquo;s network as configured fixes it. This one
gave the output <code>172.30.0.2</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span><span class="nt">wait</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">conditions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachineGuestNetworkConfigSynced</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;True&#34;</span><span class="w">
</span></span></span></code></pre></div><h3 id="checking-a-manifest-before-you-deploy-it">Checking a manifest before you deploy it</h3>
<p>The fastest check we found is a server-side dry run with strict field
validation, against the namespace the blueprint will use:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">kubectl apply --dry-run=server --validate=strict -n &lt;namespace&gt; -f vm.yaml
</span></span></code></pre></div><p>It runs the Supervisor&rsquo;s schema checks and admission webhooks, flags unknown
fields, and creates nothing. It works for every workload kind. It does <strong>not</strong>
work for the VPC kinds: VCF Automation&rsquo;s VPC API ignores <code>dryRun</code> and creates
the object, as one of our probes found.</p>
<h2 id="supervisor-namespace">Supervisor Namespace</h2>
<p><code>type: CCI.Supervisor.Namespace</code>. Creates a vSphere Namespace through VCF
Automation, inside the project&rsquo;s allocation, or adopts one that exists.
Everything in the Workload group needs one: their <code>context</code> points at it.</p>
<p>The schema has two shapes, and the validator enforces them: an existing
namespace takes <code>name</code> and <code>existing: true</code> and nothing else; a new one needs
<code>generateName</code>, <code>className</code>, <code>regionName</code> and <code>vpcName</code>.</p>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>generateName</code></td>
					<td><strong>New namespace.</strong> Prefix; VCF Automation adds <code>-</code> and five random characters (<code>ns-ref-bstk7</code>). Must match <code>^[a-z0-9]([-a-z0-9]*[a-z0-9])?$</code>, so it cannot end in a hyphen.</td>
			</tr>
			<tr>
					<td><code>className</code></td>
					<td><strong>New namespace.</strong> The namespace class: limits, VM classes, storage classes and content libraries.</td>
			</tr>
			<tr>
					<td><code>regionName</code></td>
					<td><strong>New namespace.</strong> The region.</td>
			</tr>
			<tr>
					<td><code>vpcName</code></td>
					<td><strong>New namespace.</strong> The VPC its workloads use: an existing VPC&rsquo;s name, or <code>${resource.&lt;vpc&gt;.name}</code>.</td>
			</tr>
			<tr>
					<td><code>name</code> + <code>existing: true</code></td>
					<td><strong>Existing namespace.</strong> <code>name</code> alone matches neither shape.</td>
			</tr>
			<tr>
					<td><code>zones[]</code></td>
					<td>Per vSphere Zone: <code>name</code>, <code>cpuLimit</code>, <code>cpuReservation</code>, <code>memoryLimit</code>, <code>memoryReservation</code>, all five required.</td>
			</tr>
			<tr>
					<td><code>storageClasses[]</code></td>
					<td><code>name</code> (the storage policy as the namespace names it) and <code>limit</code>.</td>
			</tr>
			<tr>
					<td><code>vmClasses[]</code>, <code>contentSources[]</code></td>
					<td>VM classes and image sources beyond the class&rsquo;s own.</td>
			</tr>
			<tr>
					<td><code>sharedSubnetNames[]</code>, <code>infraPolicyNames[]</code>, <code>segName</code>, <code>description</code></td>
					<td>Shared subnets, extra infrastructure policies, an Avi Service Engine Group, a description.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (25)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>name</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>team-a-dev</code></td>
					<td>Supervisor namespace name</td>
			</tr>
			<tr>
					<td><code>count</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>1</code></td>
					<td>The number of resource instances to be created.</td>
			</tr>
			<tr>
					<td><code>zones</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: domain-c9, cpuLimit: 4000M}]</code></td>
					<td>Zone overrides for the namespace</td>
			</tr>
			<tr>
					<td><code>zones[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>domain-c9</code></td>
					<td>Name of the zone</td>
			</tr>
			<tr>
					<td><code>zones[].cpuLimit</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>4000M</code></td>
					<td>CPU limit in M or G</td>
			</tr>
			<tr>
					<td><code>zones[].memoryLimit</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>8192Mi</code></td>
					<td>Memory limit in Mi, Gi, or Ti</td>
			</tr>
			<tr>
					<td><code>zones[].cpuReservation</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>0M</code></td>
					<td>CPU reservation in M or G</td>
			</tr>
			<tr>
					<td><code>zones[].memoryReservation</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>0Mi</code></td>
					<td>Memory reservation in Mi, Gi, or Ti</td>
			</tr>
			<tr>
					<td><code>segName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Default-Group</code></td>
					<td>Name of the Service Engine Group</td>
			</tr>
			<tr>
					<td><code>vpcName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vpc-student05</code></td>
					<td>Name of the vpc</td>
			</tr>
			<tr>
					<td><code>existing</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Use existing supervisor namespace</td>
			</tr>
			<tr>
					<td><code>className</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>nested-pod</code></td>
					<td>Name of the supervisor namespace class</td>
			</tr>
			<tr>
					<td><code>vmClasses</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: best-effort-small}]</code></td>
					<td>VM Class overrides for the namespace</td>
			</tr>
			<tr>
					<td><code>vmClasses[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>best-effort-small</code></td>
					<td>Name of the vm class</td>
			</tr>
			<tr>
					<td><code>regionName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>f06</code></td>
					<td>Name of the region</td>
			</tr>
			<tr>
					<td><code>description</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Team sandbox</code></td>
					<td>Description of the supervisor namespace</td>
			</tr>
			<tr>
					<td><code>generateName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ns-demo</code></td>
					<td>Supervisor namespace generateName</td>
			</tr>
			<tr>
					<td><code>contentSources</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: my-library, type: ContentLibrary}]</code></td>
					<td>Content Source overrides for the namespace</td>
			</tr>
			<tr>
					<td><code>contentSources[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>my-library</code></td>
					<td>Name of the content source</td>
			</tr>
			<tr>
					<td><code>contentSources[].type</code></td>
					<td>string</td>
					<td>yes</td>
					<td>default <code>ContentLibrary</code></td>
					<td>Type of the content source</td>
			</tr>
			<tr>
					<td><code>storageClasses</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: vSAN Default Storage Policy, limit: 100Gi}]</code></td>
					<td>Storage Class overrides for the namespace</td>
			</tr>
			<tr>
					<td><code>storageClasses[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vSAN Default Storage Policy</code></td>
					<td>Name of the storage class</td>
			</tr>
			<tr>
					<td><code>storageClasses[].limit</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>100Gi</code></td>
					<td>Storage Class limit in Mi, Gi, or Ti</td>
			</tr>
			<tr>
					<td><code>infraPolicyNames</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;infrastructure policy&gt;]</code></td>
					<td>Non-mandatory Infra Policy names</td>
			</tr>
			<tr>
					<td><code>sharedSubnetNames</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;shared subnet&gt;]</code></td>
					<td>Name of subnets</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Minimal, a new namespace with the zone and storage our class doesn&rsquo;t set:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">ns-demo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">className</span><span class="p">:</span><span class="w"> </span><span class="l">nested-pod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">regionName</span><span class="p">:</span><span class="w"> </span><span class="l">f06</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpcName</span><span class="p">:</span><span class="w"> </span><span class="l">vpc-student05</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">storageClasses</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vSAN Default Storage Policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">limit</span><span class="p">:</span><span class="w"> </span><span class="l">100Gi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">zones</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">domain-c9</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cpuLimit</span><span class="p">:</span><span class="w"> </span><span class="l">4000M</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cpuReservation</span><span class="p">:</span><span class="w"> </span><span class="l">0M</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">memoryLimit</span><span class="p">:</span><span class="w"> </span><span class="l">8192Mi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">memoryReservation</span><span class="p">:</span><span class="w"> </span><span class="l">0Mi</span><span class="w">
</span></span></span></code></pre></div><p><strong>Recipes</strong></p>
<ul>
<li>
<p><em>A quota sized from the request</em>, so a namespace never outgrows what was
asked for. Our lab blueprints compute the limits from the requested sizes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">    </span><span class="nt">storageClasses</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vSAN Default Storage Policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">limit</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;${input.hosts * 200 + &#39;Gi&#39;}&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">zones</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">domain-c9</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">cpuLimit</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;${input.hosts * 8000 + &#39;M&#39;}&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">cpuReservation</span><span class="p">:</span><span class="w"> </span><span class="l">0M</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">memoryLimit</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;${input.hosts * 32768 + &#39;Mi&#39;}&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">memoryReservation</span><span class="p">:</span><span class="w"> </span><span class="l">0Mi</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><em>Deploy into a namespace that already exists</em>, for example one an
administrator prepared:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">namespace</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">team-a-dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">existing</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div></li>
</ul>
<p><strong>Status worth reading:</strong> <code>${resource.&lt;ns&gt;.id}</code> is
<code>cci:&lt;project&gt;:&lt;namespace&gt;</code>, which <code>context</code> takes.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>Whether <code>zones</code> and <code>storageClasses</code> are optional depends on the namespace
class. Ours sets neither, and VCF Automation refused the minimal shape in
turn: <code>Zone should be specified in Namespace or in Namespace class</code>, then
<code>Storage Class should be specified in Namespace or in Namespace class</code>.</li>
<li>The zone <code>name</code> is the vSphere Zone. A Supervisor without zones still has
one, named after its cluster (<code>domain-c9</code> on f06).</li>
<li>A VM can only use a VM class the namespace allows and an image from its
content sources. The validator cannot know either; the request finds out.</li>
</ul>
<h2 id="vpc">VPC</h2>
<p><code>type: CCI.VPC</code>. Creates an NSX VPC in a region. Most blueprints use an
existing VPC, through the namespace&rsquo;s <code>vpcName</code>. This item is for a
blueprint that brings its own network.</p>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>generateName</code></td>
					<td><strong>Required.</strong> VCF Automation names the VPC <code>&lt;generateName&gt;-&lt;project&gt;-&lt;5 characters&gt;</code>: <code>vpc-ref-default-project-y3698</code>.</td>
			</tr>
			<tr>
					<td><code>regionName</code></td>
					<td><strong>Required.</strong> The region.</td>
			</tr>
			<tr>
					<td><code>privateIPs[]</code></td>
					<td>The VPC&rsquo;s private CIDRs. Empty uses the project&rsquo;s default.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (4)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>count</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>1</code></td>
					<td>The number of resource instances to be created.</td>
			</tr>
			<tr>
					<td><code>privateIPs</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.0/20]</code></td>
					<td>List of private IPs to be used in the VPC</td>
			</tr>
			<tr>
					<td><code>regionName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>f06</code></td>
					<td>Region name for the VPC</td>
			</tr>
			<tr>
					<td><code>generateName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vpc-app</code></td>
					<td>Prefix for the generated name of the VPC</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vpc</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">vpc-app</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">regionName</span><span class="p">:</span><span class="w"> </span><span class="l">f06</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">privateIPs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="m">10.200.0.0</span><span class="l">/20</span><span class="w">
</span></span></span></code></pre></div><p><strong>Status worth reading:</strong> <code>id</code> (<code>cci:vpc:&lt;project&gt;:&lt;name&gt;</code>), which every VPC
Configuration takes as <code>vpc</code>, and <code>name</code>, which a namespace takes as
<code>vpcName</code>.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>A new VPC reaches nothing outside itself until it has an
<a href="#attachment">Attachment</a> to a VPC connectivity profile; give the namespace
<code>dependsOn</code> on the attachment.</li>
<li><code>privateIPs</code> must not overlap the connectivity profile&rsquo;s Private Transit
Gateway blocks, and the error only comes at attachment time: <code>private IP CIDR 172.31.64.0/20 overlaps with private TGW IP block CIDR 172.31.0.0/16</code>.</li>
<li><strong>A blueprint cannot give its VPC a load balancer.</strong> In VCF Automation&rsquo;s
VPC API the load balancer is its own kind, <code>LoadBalancer</code>, and
<code>CCI.VPC.Configuration</code> refuses it: <code>Failed to match exactly one schema (matched 0 out of 5)</code>. Without one, a LoadBalancer service never gets an
address (and its request never finishes, see <a href="#wait-when-a-resource-is-finished"><code>wait</code></a>),
and Broadcom&rsquo;s VPC page warns that a VPC without load balancing cannot run
VKS. For either, use a VPC made in the UI with <strong>Enable load balancing</strong>
on, and name it in the namespace&rsquo;s <code>vpcName</code>.</li>
</ul>
<h2 id="vpc-configuration">VPC Configuration</h2>
<p><code>type: CCI.VPC.Configuration</code>. One item for every object that lives inside a
VPC, and <code>kind</code> chooses which. The five palette entries below are this item
with <code>kind</code> filled in. Each <code>configs[]</code> entry becomes one object, so one
resource can create several rules or groups of the same kind.</p>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>vpc</code></td>
					<td><strong>Required.</strong> The VPC&rsquo;s id: <code>${resource.vpc.id}</code>, or an existing VPC&rsquo;s <code>cci:vpc:&lt;project&gt;:&lt;name&gt;</code>.</td>
			</tr>
			<tr>
					<td><code>kind</code></td>
					<td><strong>Required.</strong> <code>VPCAttachment</code>, <code>VPCIPAddressAllocation</code>, <code>VPCNATRule</code>, <code>VPCNetworkSecurityGroup</code> or <code>VPCGatewayFirewallPolicy</code>, and nothing else.</td>
			</tr>
			<tr>
					<td><code>apiVersion</code></td>
					<td><code>vpc.nsx.vmware.com/v1alpha1</code>.</td>
			</tr>
			<tr>
					<td><code>configs[]</code></td>
					<td><strong>Required.</strong> Per object: <code>generateName</code> (<strong>required</strong>), <code>spec</code>, <code>labels</code>, <code>annotations</code>.</td>
			</tr>
			<tr>
					<td><code>wait</code></td>
					<td>Applies to every object in <code>configs</code>.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (20)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>vpc</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>${resource.vpc.id}</code></td>
					<td>ID of the parent VPC this resource is associated with.</td>
			</tr>
			<tr>
					<td><code>kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>VPCNATRule</code></td>
					<td>The kind of the resource (e.g., VPCNetworkSecurityGroup, VPCIPAddressAllocation, VPCNATRule, VPCAttachment).</td>
			</tr>
			<tr>
					<td><code>wait</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{fields: [{path: status.conditions[0].status, value: True}], ...}</code></td>
					<td>Wait conditions applied to all config resources. All resources must satisfy these conditions before the operation is considered complete.</td>
			</tr>
			<tr>
					<td><code>wait.fields</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{path: status.conditions[0].status, value: True}]</code></td>
					<td>List of field conditions to wait for.</td>
			</tr>
			<tr>
					<td><code>wait.fields[].path</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>status.conditions[0].status</code></td>
					<td>JSONPath to the field to check (e.g., status.phase).</td>
			</tr>
			<tr>
					<td><code>wait.fields[].value</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>True</code></td>
					<td>The expected value of the field. Use &lsquo;*&rsquo; for any non-null value.</td>
			</tr>
			<tr>
					<td><code>wait.fields[].indicatesFailure</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Whether this field condition indicates a failure state.</td>
			</tr>
			<tr>
					<td><code>wait.conditions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{type: Realized, reason: &lt;condition reason&gt;}]</code></td>
					<td>List of status conditions to wait for.</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].type</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>Realized</code></td>
					<td>The type of the condition (e.g., Ready, Realized).</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].reason</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;condition reason&gt;</code></td>
					<td>Optional reason for the condition.</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].status</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>True</code></td>
					<td>The expected status of the condition (e.g., True, False).</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].indicatesFailure</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Whether this condition indicates a failure state.</td>
			</tr>
			<tr>
					<td><code>wait.skipWaitOnDelete</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Whether to skip waiting for conditions during delete operations.</td>
			</tr>
			<tr>
					<td><code>count</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>1</code></td>
					<td>The number of resource instances to be created.</td>
			</tr>
			<tr>
					<td><code>configs</code></td>
					<td>array of object</td>
					<td>yes</td>
					<td>e.g. <code>[{generateName: dnat-web, spec: {action: DNAT, translatedNetwork: 10.200.0.10}}]</code></td>
					<td>List of resources associated with the VPC.</td>
			</tr>
			<tr>
					<td><code>configs[].spec</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{action: DNAT, translatedNetwork: 10.200.0.10}</code></td>
					<td>The specification of the associated resource.</td>
			</tr>
			<tr>
					<td><code>configs[].labels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>Labels for categorizing the resource.</td>
			</tr>
			<tr>
					<td><code>configs[].annotations</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{owner: team-a}</code></td>
					<td>Annotations for additional metadata about the resource.</td>
			</tr>
			<tr>
					<td><code>configs[].generateName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>dnat-web</code></td>
					<td>A prefix for generating a unique name for the resource.</td>
			</tr>
			<tr>
					<td><code>apiVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vpc.nsx.vmware.com/v1alpha1</code></td>
					<td>The API version of the resource.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Three things hold for all five kinds:</p>
<ul>
<li><strong><code>generateName</code> is the name, not a prefix.</strong> VCF Automation names the
object <code>&lt;vpc&gt;:&lt;generateName&gt;</code>, as written: our group was
<code>vpc-ref-default-project-y3698:web</code>. Keep it unique per kind in the VPC.</li>
<li><strong>VCF Automation fills in <code>spec.vpcName</code> and <code>spec.regionName</code></strong> from the
<code>vpc</code> property; leave them out.</li>
<li><strong>Another resource reads an object as <code>configs[n]</code></strong>:
<code>${resource.webGroup.configs[0].name}</code> is the full name a firewall rule
needs, and <code>${resource.publicIp.configs[0].spec.allocationIPs}</code> is the
address an allocation got.</li>
</ul>
<p>The shape, for every kind:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">natRules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC.Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpc</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vpc.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPCNATRule</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">dnat-web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="l">... }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">dnat-ssh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="l">... }</span><span class="w">
</span></span></span></code></pre></div><h3 id="attachment">Attachment</h3>
<p><code>kind: VPCAttachment</code>. Attaches the VPC to a VPC connectivity profile, which
decides its transit gateway, its external IP blocks and whether it gets a
default outbound NAT.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>vpcConnectivityProfileName</code></td>
					<td><strong>Required.</strong> The profile, as NSX names it (f06&rsquo;s is <code>default--f06</code>).</td>
			</tr>
			<tr>
					<td><code>preferredDefaultSNATIP</code></td>
					<td>The address for the VPC&rsquo;s automatic SNAT. It must be free in the external block; empty lets NSX choose.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (2)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.preferredDefaultSNATIP</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>192.168.144.30</code></td>
					<td>PreferredDefaultSNATIP specifies the translated IP for VPC auto SNAT rules. The specified IP must be available.</td>
			</tr>
			<tr>
					<td><code>spec.vpcConnectivityProfileName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>default--f06</code></td>
					<td>VPCConnectivityProfileName specifies the name of the VPC Connectivity Profile associated with the VPC.</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">attach</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC.Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpc</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vpc.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPCAttachment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">attach</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">vpcConnectivityProfileName</span><span class="p">:</span><span class="w"> </span><span class="l">default--f06</span><span class="w">
</span></span></span></code></pre></div><p>With the attachment realized, NSX adds the VPC&rsquo;s default SNAT rule and its
address by itself (ours: <code>10.200.0.0/20</code> to <code>192.168.144.14</code>).</p>
<h3 id="ip-address-allocation">IP Address Allocation</h3>
<p><code>kind: VPCIPAddressAllocation</code>. Reserves addresses from one of the VPC&rsquo;s IP
blocks, typically an external address for a NAT rule.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>ipAddressBlockVisibility</code></td>
					<td><code>Private</code> (default), <code>PrivateTGW</code> or <code>External</code>. The NSX API&rsquo;s own default is <code>External</code>.</td>
			</tr>
			<tr>
					<td><code>allocationSize</code></td>
					<td>How many addresses, a power of 2. Either this or <code>allocationIPs</code>.</td>
			</tr>
			<tr>
					<td><code>allocationIPs</code></td>
					<td>Specific addresses, as a CIDR (<code>192.168.0.1/32</code>).</td>
			</tr>
			<tr>
					<td><code>ipBlockName</code></td>
					<td>A particular block, when the visibility has more than one.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (4)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.allocationIPs</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>192.168.144.18</code></td>
					<td>The specific IP addresses from IPBlock that needs to be requested. If specified, it should be passed like 192.168.0.0/24 or 192.168.0.1/32.</td>
			</tr>
			<tr>
					<td><code>spec.allocationSize</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1</code></td>
					<td>Allocation IP address size for auto allocating IPs from IPBlock. The IP addresses will be auto allocated from unused IP addresses based on allocation size.</td>
			</tr>
			<tr>
					<td><code>spec.ipAddressBlockVisibility</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>External</code></td>
					<td>Visibility of IP address block. Must be External, Private or PrivateTGW. Note: the default Private Visibility is different from NSX API&rsquo;s default External Visibility.</td>
			</tr>
			<tr>
					<td><code>spec.ipBlockName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>:f06-vpc-ext02</code></td>
					<td>IPBlock name for allocating IP address.</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">publicIp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC.Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">dependsOn</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">attach]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpc</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vpc.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPCIPAddressAllocation</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">web-ip</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">ipAddressBlockVisibility</span><span class="p">:</span><span class="w"> </span><span class="l">External</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">allocationSize</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span></code></pre></div><p>The allocated address appears in the object&rsquo;s own spec:
<code>${resource.publicIp.configs[0].spec.allocationIPs}</code> gave <code>192.168.144.18</code>.
An external allocation needs the attachment first, hence the <code>dependsOn</code>.</p>
<h3 id="nat-rule">NAT Rule</h3>
<p><code>kind: VPCNATRule</code>. A NAT rule on the VPC&rsquo;s gateway. The designer&rsquo;s default
<code>wait</code> is <code>Realized</code>.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>action</code></td>
					<td><strong>Required.</strong> <code>SNAT</code>, <code>DNAT</code>, <code>Reflexive</code>, <code>NoSNAT</code> or <code>NoDNAT</code>.</td>
			</tr>
			<tr>
					<td><code>translatedNetwork</code></td>
					<td><strong>Required.</strong> For SNAT, one address from the VPC&rsquo;s external block.</td>
			</tr>
			<tr>
					<td><code>sourceNetwork</code></td>
					<td>One address, a comma-separated list, or a CIDR. Mandatory for SNAT.</td>
			</tr>
			<tr>
					<td><code>destinationNetwork</code></td>
					<td>One address; empty means any.</td>
			</tr>
			<tr>
					<td><code>serviceEntry</code></td>
					<td><code>protocol</code> (<code>TCP</code>, <code>UDP</code>, <code>ICMP</code>), <code>sourcePorts</code>, <code>destinationPorts</code>, <code>translatedPorts</code>. See the warning.</td>
			</tr>
			<tr>
					<td><code>sequenceNumber</code></td>
					<td>Priority, default 0.</td>
			</tr>
			<tr>
					<td><code>firewallMatch</code></td>
					<td><code>MatchInternalAddress</code> (default), <code>MatchExternalAddress</code> or <code>ByPass</code>.</td>
			</tr>
			<tr>
					<td><code>enabled</code>, <code>logging</code></td>
					<td>Defaults <code>true</code> and <code>false</code>.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (13)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.action</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>DNAT</code></td>
					<td>Action represents action of NAT Rule. Valid values: SNAT, DNAT, Reflexive, NoSNAT and NoDNAT.</td>
			</tr>
			<tr>
					<td><code>spec.destinationNetwork</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>192.168.144.18</code></td>
					<td>DestinationNetwork represents the destination network. The value can be a single IPv4 address or CIDR, or a comma separated list of IPv4 addresses.</td>
			</tr>
			<tr>
					<td><code>spec.enabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>NAT Rule enabled flag Enabled indicates whether the NAT rule is enabled or disabled. The default is True.</td>
			</tr>
			<tr>
					<td><code>spec.firewallMatch</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>MATCH_INTERNAL_ADDRESS</code></td>
					<td>FirewallMatch indicates how the firewall matches the address after NATing if firewall stage is not skipped.</td>
			</tr>
			<tr>
					<td><code>spec.logging</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>NAT Rule logging flag Logging indicates whether the logging of NAT rule is enabled or disabled. The default is False.</td>
			</tr>
			<tr>
					<td><code>spec.sequenceNumber</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>0</code></td>
					<td>SequenceNumber decides the priority of a NAT rule. Valid range is [0, 2147481599]. Default is 0.</td>
			</tr>
			<tr>
					<td><code>spec.serviceEntry</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{destinationPorts: &quot;22&quot;, protocol: TCP}</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.serviceEntry.destinationPorts</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&quot;22&quot;</code></td>
					<td>The destination ports to match. If specified, it must be either a single port (e.g. &ldquo;8080&rdquo;) or a port range (e.g. &ldquo;8090-8095&rdquo;).</td>
			</tr>
			<tr>
					<td><code>spec.serviceEntry.protocol</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>TCP</code></td>
					<td>Protocol supports TCP, UDP and ICMP v4.</td>
			</tr>
			<tr>
					<td><code>spec.serviceEntry.sourcePorts</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>1024-65535</code></td>
					<td>The source ports to match. If specified, it must be either a single port (e.g. &ldquo;8080&rdquo;) or a port range (e.g. &ldquo;8090-8095&rdquo;).</td>
			</tr>
			<tr>
					<td><code>spec.serviceEntry.translatedPorts</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&quot;2222&quot;</code></td>
					<td>The translated ports. If specified, it must be either a single port (e.g. &ldquo;8080&rdquo;) or a port range (e.g. &ldquo;8090-8095&rdquo;).</td>
			</tr>
			<tr>
					<td><code>spec.sourceNetwork</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10.200.0.0/28</code></td>
					<td>SourceNetwork represents the source network address. The value can be a single IPv4 address or CIDR, or a comma separated list of IPv4 addresses.</td>
			</tr>
			<tr>
					<td><code>spec.translatedNetwork</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>10.200.0.10</code></td>
					<td>TranslatedNetwork represents the translated network address. The field is required and must contain a single IPv4 address for SNAT, DNAT and Reflexive.</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dnatSsh</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC.Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpc</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vpc.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPCNATRule</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">dnat-ssh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">action</span><span class="p">:</span><span class="w"> </span><span class="l">DNAT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">destinationNetwork</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.publicIp.configs[0].spec.allocationIPs}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">translatedNetwork</span><span class="p">:</span><span class="w"> </span><span class="m">10.200.0.10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">serviceEntry</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">destinationPorts</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;22&#34;</span><span class="w">
</span></span></span></code></pre></div><p><strong>Warning: the port did not reach NSX.</strong> VCF Automation&rsquo;s API kept the
<code>serviceEntry</code> (TCP 22), but the rule NSX realized had <code>service: null</code>: a
DNAT of every port on <code>192.168.144.18</code> to the private address. Treat a NAT
rule as a whole-address mapping. To publish one port, use a
<a href="#virtual-machine-service">Virtual Machine Service</a> of type <code>LoadBalancer</code>,
which forwards only its ports and follows the VM&rsquo;s address.</p>
<h3 id="group">Group</h3>
<p><code>kind: VPCNetworkSecurityGroup</code>. A group of addresses, VMs or pods that
firewall rules can name. Default <code>wait</code>: <code>Realized</code>.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>ipAddresses[]</code></td>
					<td>Addresses, ranges or CIDRs.</td>
			</tr>
			<tr>
					<td><code>vmSelectors[]</code></td>
					<td><code>labelSelector</code> (VMs by label, so new VMs with the label join), <code>namespaceSelector</code>, and <code>propertySelector</code> (VMs by <code>Name</code>, <code>OSName</code> or <code>ComputerName</code>, with <code>Equals</code>, <code>Contains</code>, <code>StartsWith</code>, <code>EndsWith</code>, <code>NotEquals</code>).</td>
			</tr>
			<tr>
					<td><code>podSelectors[]</code></td>
					<td><code>labelSelector</code> and <code>namespaceSelector</code> for pods.</td>
			</tr>
			<tr>
					<td><code>vms[]</code></td>
					<td>Specific VMs, by <code>instanceUUID</code>.</td>
			</tr>
			<tr>
					<td><code>vpcNetworkSecurityGroupNames[]</code></td>
					<td>Other groups, nested.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (35)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.ipAddresses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.0/28]</code></td>
					<td>List of IPs or CIDRs to be included in this VPCNetworkSecurityGroup. Each entry can be a single IP address, an IP range, or a subnet in CIDR notation.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{labelSelector: {matchLabels: {app: web}}, ...}]</code></td>
					<td>List of Pod label selectors that will dynamically select Pods to include in this VPCNetworkSecurityGroup.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].labelSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchLabels: {app: web}}</code></td>
					<td>A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].labelSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].labelSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].labelSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].labelSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].labelSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].namespaceSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchExpressions: [{key: app, operator: In}], matchLabels: {app: web}}</code></td>
					<td>A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].namespaceSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].namespaceSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].namespaceSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].namespaceSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.podSelectors[].namespaceSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{labelSelector: {matchLabels: {app: web}}, ...}]</code></td>
					<td>List of Virtual Machine label selectors that will dynamically select VMs to include in this VPCNetworkSecurityGroup.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].labelSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchLabels: {app: web}}</code></td>
					<td>A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].labelSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].labelSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].labelSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].labelSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].labelSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].namespaceSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchExpressions: [{key: app, operator: In}], matchLabels: {app: web}}</code></td>
					<td>A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].namespaceSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].namespaceSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].namespaceSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].namespaceSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].namespaceSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].propertySelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchExpressions: [{key: Name, operator: StartsWith}]}</code></td>
					<td>PropertySelector represents a set of conditions on VM properties. All MatchExpressions are ANDed; a VM must satisfy all expressions to match.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].propertySelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: Name, operator: StartsWith}]</code></td>
					<td>MatchExpressions is a list of property selector requirements. Each requirement consists of a key, operator, and value.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].propertySelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>Name</code></td>
					<td>Key is the VM property to match. Valid keys are Name, OSName and ComputerName.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].propertySelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>StartsWith</code></td>
					<td>Operator defines how the Key is compared against Value. Valid operators are Equals, Contains, StartsWith, EndsWith and NotEquals.</td>
			</tr>
			<tr>
					<td><code>spec.vmSelectors[].propertySelector.matchExpressions[].value</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>web-</code></td>
					<td>Value is the target value to match against the VM property.</td>
			</tr>
			<tr>
					<td><code>spec.vms</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{instanceUUID: 5010c9b4-1f2e-4d3c-8b7a-6e5f4d3c2b1a}]</code></td>
					<td>List of Virtual Machine references that will be included in this VPCNetworkSecurityGroup.</td>
			</tr>
			<tr>
					<td><code>spec.vms[].instanceUUID</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>5010c9b4-1f2e-4d3c-8b7a-6e5f4d3c2b1a</code></td>
					<td>InstanceUUID of the VM being referenced.</td>
			</tr>
			<tr>
					<td><code>spec.vpcNetworkSecurityGroupNames</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[db-servers]</code></td>
					<td>List of VPCNetworkSecurityGroup names that will be included in this VPCNetworkSecurityGroup.</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">webGroup</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC.Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpc</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vpc.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPCNetworkSecurityGroup</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">vmSelectors</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">labelSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">tier</span><span class="p">:</span><span class="w"> </span><span class="l">web}</span><span class="w">
</span></span></span></code></pre></div><p>Every VPC also has a group named <code>default</code>, made by NSX.</p>
<h3 id="gateway-firewall-policy">Gateway Firewall Policy</h3>
<p><code>kind: VPCGatewayFirewallPolicy</code>. Rules on the VPC&rsquo;s gateway, for traffic
entering and leaving the VPC. The distributed firewall inside the VPC is a
separate thing.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>rules[]</code></td>
					<td>Per rule: <code>name</code> (unique in the policy), <code>action</code> (<code>Allow</code> default, <code>Drop</code>, <code>Reject</code>, <code>JumpToApplication</code>), <code>direction</code> (<code>InOut</code> default, <code>In</code>, <code>Out</code>), <code>from[]</code> and <code>to[]</code> (each entry <code>groupName</code> or <code>ipAddress</code>), <code>services[]</code> (<code>networkServiceName</code>, or <code>l4PortSet</code> with <code>l4Protocol</code>, <code>destinationPorts</code>, <code>sourcePorts</code>), <code>ipProtocol</code>, <code>log</code>, <code>disabled</code>, <code>notes</code>, <code>tag</code>, <code>sourcesExcluded</code>, <code>destinationsExcluded</code>, <code>appliedTo</code>.</td>
			</tr>
			<tr>
					<td><code>category</code></td>
					<td><code>LocalGatewayRules</code> (default) or <code>Default</code>.</td>
			</tr>
			<tr>
					<td><code>priority</code></td>
					<td>Order against other policies, default 0.</td>
			</tr>
			<tr>
					<td><code>stateful</code>, <code>tcpStrict</code></td>
					<td>Stateful inspection; a full TCP handshake before data.</td>
			</tr>
			<tr>
					<td><code>description</code>, <code>locked</code></td>
					<td></td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (35)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.category</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>LocalGatewayRules</code></td>
					<td>Pre-defined categories for classifying a VPC Gateway Firewall policy.There are two pre-defined categories. They are &ldquo;LocalGatewayRules&rdquo; and &ldquo;Default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.description</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Inbound HTTPS</code></td>
					<td>Description for the firewall policy.</td>
			</tr>
			<tr>
					<td><code>spec.isDefault</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>A flag to indicate whether rule is a default rule</td>
			</tr>
			<tr>
					<td><code>spec.locked</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Locked indicates whether a security policy should be locked</td>
			</tr>
			<tr>
					<td><code>spec.priority</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>0</code></td>
					<td>This field is used to resolve conflicts between multiple Rules under Security or Gateway Policy for a Domain. If no priority is specified in the payload, a value of 0 is assigned by default.</td>
			</tr>
			<tr>
					<td><code>spec.rules</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{action: Allow, name: https-in}]</code></td>
					<td>Rules that are a part of this FirewallPolicy</td>
			</tr>
			<tr>
					<td><code>spec.rules[].action</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Allow</code></td>
					<td>Action to be applied to all the services</td>
			</tr>
			<tr>
					<td><code>spec.rules[].appliedTo</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{gatewayAttachmentNames: [&lt;transit gateway attachment&gt;], ...}</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.rules[].appliedTo.gatewayAttachmentNames</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;transit gateway attachment&gt;]</code></td>
					<td>This field is only applicable when the rule is defined for Transit Gateway Firewall policy</td>
			</tr>
			<tr>
					<td><code>spec.rules[].appliedTo.gatewayNames</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;transit gateway&gt;]</code></td>
					<td>This field is only applicable when the rule is defined for Transit Gateway Firewall policy</td>
			</tr>
			<tr>
					<td><code>spec.rules[].appliedTo.groupNames</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;group&gt;]</code></td>
					<td>This field is only applicable when the rule is defined for Distributed Firewall policy</td>
			</tr>
			<tr>
					<td><code>spec.rules[].destinationsExcluded</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>DestinationsExcluded indicates that the rule applies to all destinations <em>except</em> those specified in the &lsquo;To&rsquo; field.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].direction</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>In</code></td>
					<td>Direction defines direction of traffic.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].disabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Disabled indicates if the rule is enabled/disabled.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].from</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{ipAddress: 0.0.0.0/0, groupName: admin-hosts}]</code></td>
					<td>From defines the source of the traffic. If empty, it defaults to &ldquo;Any&rdquo;, matching all sources.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].from[].groupName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>admin-hosts</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.rules[].from[].ipAddress</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>0.0.0.0/0</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.rules[].ipProtocol</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>IPV4</code></td>
					<td>IpProtocol indicates type of IP packet that should be matched while enforcing the rule. Only IPV_4 protocol is supported for new rules, IPV4_IPV6 is only allowed for default rules.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].isDefault</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>IsDefault is a flag to indicate whether rule is a default rule.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].log</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Log indicates if traffic matching this rule should be logged.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>https-in</code></td>
					<td>Name for the rule. Must be unique within the policy.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].notes</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>HTTPS from anywhere</code></td>
					<td>Notes for the rule.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].services</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{l4PortSet: {destinationPorts: [443], l4Protocol: TCP}, networkServiceName: :HTTPS}]</code></td>
					<td>Services specifies the network services (protocols and ports) to which this rule applies. If empty or null ,it defaults to &ldquo;Any&rdquo; , then this rule applies to all services.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].services[].l4PortSet</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{destinationPorts: [443], l4Protocol: TCP}</code></td>
					<td>L4PortSetServiceEntry is a ServiceEntry that represents TCP or UDP protocol.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].services[].l4PortSet.destinationPorts</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[443]</code></td>
					<td>DestinationPorts defines the destination port or port range to match. For example: [&ldquo;443&rdquo;], [&ldquo;8080-8090&rdquo;]. If empty, matches any destination port.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].services[].l4PortSet.l4Protocol</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>TCP</code></td>
					<td>L4Protocol specifies the Layer 4 protocol (TCP or UDP).</td>
			</tr>
			<tr>
					<td><code>spec.rules[].services[].l4PortSet.sourcePorts</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[1000-2000]</code></td>
					<td>SourcePorts defines the source port or port range to match. For example: [&ldquo;80&rdquo;], [&ldquo;1000-2000&rdquo;]. If empty, matches any source port.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].services[].networkServiceName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>:HTTPS</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.rules[].sourcesExcluded</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>SourcesExcluded indicates that the rule applies to all sources <em>except</em> those specified in the &lsquo;From&rsquo; field. When true, the &lsquo;From&rsquo; field acts as an exclusion list.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].tag</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>web</code></td>
					<td>Tag applied on the rule.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].to</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{groupName: ${resource.webGroup.configs[0].name}, ipAddress: 10.200.0.10}]</code></td>
					<td>To defines the destination of the traffic. If empty, it defaults to &ldquo;Any&rdquo;, matching all destinations.</td>
			</tr>
			<tr>
					<td><code>spec.rules[].to[].groupName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>${resource.webGroup.configs[0].name}</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.rules[].to[].ipAddress</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10.200.0.10</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.stateful</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Stateful or Stateless nature of security policy is enforced on all rules in this security policy.</td>
			</tr>
			<tr>
					<td><code>spec.tcpStrict</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Ensures that a 3 way TCP handshake is done before the data packets are sent. tcp_strict=true is supported only for stateful security policies.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Recipe, HTTPS from anywhere to the web group:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">gwPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.VPC.Configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">dependsOn</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">attach]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">vpc</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.vpc.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPCGatewayFirewallPolicy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">web-in</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">stateful</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">https-in</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">action</span><span class="p">:</span><span class="w"> </span><span class="l">Allow</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">direction</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">from</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span>- <span class="nt">ipAddress</span><span class="p">:</span><span class="w"> </span><span class="m">0.0.0.0</span><span class="l">/0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">to</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span>- <span class="nt">groupName</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.webGroup.configs[0].name}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span>- <span class="nt">networkServiceName</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:HTTPS&#34;</span><span class="w">
</span></span></span></code></pre></div><p><strong>Gotchas</strong></p>
<ul>
<li><strong><code>from</code> is required</strong>, whatever the field&rsquo;s description says: without it,
<code>spec.rules[0].from: Required value</code>. Write <code>0.0.0.0/0</code> for any source.</li>
<li><strong>Name a service rather than a port set.</strong> A rule that lists only an
<code>l4PortSet</code> gets <code>networkServiceName: Any</code> added by the API, and NSX
realizes it as services <code>ANY</code> beside the raw TCP 443 entry. With
<code>networkServiceName: &quot;:HTTPS&quot;</code> NSX holds exactly <code>/infra/services/HTTPS</code>.
The API lists 415 services, all with a leading colon (<code>:DNS</code>, <code>:HTTPS</code>,
<code>:SSH</code>); without the colon it refuses: <code>Network service name must start with a colon (:), such as :HTTP</code>.</li>
<li><code>groupName</code> takes the group&rsquo;s full name, <code>&lt;vpc&gt;:&lt;generateName&gt;</code>, which
<code>configs[0].name</code> supplies.</li>
<li>The gateway firewall has to be active in the VPC&rsquo;s security profile for any
of this to be enforced.</li>
</ul>
<h2 id="supervisor-resource">Supervisor Resource</h2>
<p><code>type: CCI.Supervisor.Resource</code>. Any Kubernetes object in the namespace,
described by <code>manifest</code>. Every workload item that follows is this type with
<code>apiVersion</code> and <code>kind</code> filled in, so everything here applies to them.</p>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>context</code></td>
					<td><strong>Required.</strong> <code>${resource.&lt;namespace&gt;.id}</code>.</td>
			</tr>
			<tr>
					<td><code>manifest</code></td>
					<td><strong>Required.</strong> The object: <code>apiVersion</code>, <code>kind</code>, <code>metadata</code>, <code>spec</code>. A change to <code>manifest</code> or <code>context</code> recreates the object.</td>
			</tr>
			<tr>
					<td><code>wait</code></td>
					<td>As above.</td>
			</tr>
			<tr>
					<td><code>existing</code></td>
					<td><code>true</code> adopts an object that already exists.</td>
			</tr>
			<tr>
					<td><code>object</code></td>
					<td>Computed: the live object.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (18)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>wait</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{fields: [{path: status.powerState, value: PoweredOn}], ...}</code></td>
					<td>resource yaml</td>
			</tr>
			<tr>
					<td><code>wait.fields</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{path: status.powerState, value: PoweredOn}]</code></td>
					<td>List of fields for whose value needs to be waited for resource to be finished</td>
			</tr>
			<tr>
					<td><code>wait.fields[].path</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>status.powerState</code></td>
					<td>The path of the field within the Kubernetes resource</td>
			</tr>
			<tr>
					<td><code>wait.fields[].value</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>PoweredOn</code></td>
					<td>The value that needs to be met for the wait to be finished.</td>
			</tr>
			<tr>
					<td><code>wait.fields[].indicatesFailure</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>When the condition is met, indicates failure if set to true</td>
			</tr>
			<tr>
					<td><code>wait.conditions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{type: VirtualMachineGuestNetworkConfigSynced, status: True}]</code></td>
					<td>List of conditions that indicate success/failure of resource</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].type</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>VirtualMachineGuestNetworkConfigSynced</code></td>
					<td>The condition type for which to wait</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].reason</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;condition reason&gt;</code></td>
					<td>The condition reason for which to wait</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].status</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>True</code></td>
					<td>The value of the condition that needs to be met</td>
			</tr>
			<tr>
					<td><code>wait.conditions[].indicatesFailure</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>When the condition is met, indicates failure if set to true</td>
			</tr>
			<tr>
					<td><code>wait.executionLogs</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{failureMessagePattern: (?i)error, progressMessagePattern: (?i)creating}</code></td>
					<td>The message to fetch from the logs while the resource is being created. This is only supported for Kubernetes Jobs.</td>
			</tr>
			<tr>
					<td><code>wait.executionLogs.failureMessagePattern</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(?i)error</code></td>
					<td>The message pattern to check for to fail the resource creation. If the message is found in the logs, the resource creation will be marked as failed.</td>
			</tr>
			<tr>
					<td><code>wait.executionLogs.progressMessagePattern</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>(?i)creating</code></td>
					<td>The message pattern to check for to indicate that the resource creation is in progress. If the message is found in the logs, it will be shown as part of the deployment.</td>
			</tr>
			<tr>
					<td><code>wait.skipWaitOnDelete</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>If false, do not wait for resources to be gone before completing</td>
			</tr>
			<tr>
					<td><code>count</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>1</code></td>
					<td>The number of resource instances to be created.</td>
			</tr>
			<tr>
					<td><code>context</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>${resource.namespace.id}</code></td>
					<td>The CCI.Supervisor.Namespace resource id</td>
			</tr>
			<tr>
					<td><code>existing</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Use existing supervisor namespace</td>
			</tr>
			<tr>
					<td><code>manifest</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{apiVersion: vmoperator.vmware.com/v1alpha5, kind: VirtualMachine, spec: ...}</code></td>
					<td>The yaml representation of the Kubernetes resource</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Recipe, a kind the palette doesn&rsquo;t have. Our labs carry three VLANs over one
trunk subnet with binding maps:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">bmMgmt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">crd.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">SubnetConnectionBindingMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">bm-mgmt}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span>{<span class="nt">subnetName</span><span class="p">:</span><span class="w"> </span><span class="nt">sn-mgmt, targetSubnetName</span><span class="p">:</span><span class="w"> </span><span class="nt">sn-trunk, vlanTrafficTag</span><span class="p">:</span><span class="w"> </span><span class="m">1610</span>}<span class="w">
</span></span></span></code></pre></div><p>On the 9.1 Supervisor the namespace&rsquo;s API also offers, among others,
<code>VirtualMachineReplicaSet</code>, <code>VirtualMachineSnapshot</code>, <code>VirtualMachineImage</code>,
<code>SubnetSet</code>, <code>ConfigMap</code> and, with Avi, the Gateway API kinds.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li><code>manifest</code> can be a string as well as a map. Our generator writes one per
host as a string, because a VM with optional sections is easier to build as
text: <code>manifest: &quot;${...}&quot;</code> works as long as the expression returns valid
YAML.</li>
<li>Broadcom&rsquo;s day-2 page warns that bindings don&rsquo;t work for Supervisor
Resources in day-2 operations; a day-2 action has to take the resource as
an input.</li>
</ul>
<h2 id="virtual-machine">Virtual Machine</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: vmoperator.vmware.com/v1alpha5</code>,
<code>kind: VirtualMachine</code>. A VM Service VM: built from a VM class (CPU, memory,
devices) and an image, and configured on first boot by cloud-init, Sysprep,
LinuxPrep or vApp properties.</p>
<p>The most used fields; the complete list of 266 follows.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>className</code></td>
					<td>The VM class. Changing it later resizes the VM.</td>
			</tr>
			<tr>
					<td><code>imageName</code></td>
					<td>The image: its resource name (<code>vmi-0f0136a489b21d06c</code>) or its display name (<code>ubuntu-24.04-server-cloudimg-amd64</code>), if that is unique among the namespace&rsquo;s and the cluster&rsquo;s images.</td>
			</tr>
			<tr>
					<td><code>storageClass</code></td>
					<td>The storage class for the VM&rsquo;s disks.</td>
			</tr>
			<tr>
					<td><code>powerState</code></td>
					<td><code>PoweredOn</code> (default), <code>PoweredOff</code>, <code>Suspended</code>.</td>
			</tr>
			<tr>
					<td><code>guestID</code></td>
					<td>The guest OS identifier. <strong>Required when the VM has a CD-ROM.</strong> Immutable while powered on.</td>
			</tr>
			<tr>
					<td><code>network</code></td>
					<td><code>hostName</code>, <code>domainName</code>, <code>nameservers</code>, <code>searchDomains</code>, <code>disabled</code>, and <code>interfaces[]</code>: <code>name</code> (required), <code>network</code> (a Subnet or SubnetSet), <code>addresses[]</code>, <code>gateway4</code>, <code>dhcp4</code>, <code>mtu</code>, <code>routes[]</code>, <code>nameservers[]</code>, <code>searchDomains[]</code>, <code>guestDeviceName</code>, <code>macAddr</code>. Without <code>interfaces</code>, the VM joins the namespace&rsquo;s default network.</td>
			</tr>
			<tr>
					<td><code>bootstrap</code></td>
					<td>One of <code>cloudInit</code> (inline <code>cloudConfig</code>, <code>rawCloudConfig</code> from a Secret, <code>sshAuthorizedKeys</code>), <code>sysprep</code> (inline or <code>rawSysprep</code> from a Secret), <code>linuxPrep</code> (<code>timeZone</code>, <code>hardwareClockIsUTC</code>, <code>password</code>, <code>scriptText</code>), <code>vAppConfig</code> (<code>properties</code>, <code>rawProperties</code>).</td>
			</tr>
			<tr>
					<td><code>volumes[]</code></td>
					<td>Extra disks from Persistent Volume Claims: <code>name</code>, <code>persistentVolumeClaim.claimName</code>, and per volume <code>controllerType</code> (<code>SCSI</code> default, <code>NVME</code>, <code>SATA</code>, <code>IDE</code>), <code>controllerBusNumber</code>, <code>unitNumber</code>, <code>diskMode</code>, <code>sharingMode</code>, <code>applicationType</code>, <code>removable</code>.</td>
			</tr>
			<tr>
					<td><code>hardware</code></td>
					<td><code>cdrom[]</code> (an ISO image, <code>connected</code>, <code>allowGuestControl</code>) and the controllers: <code>scsiControllers[]</code>, <code>nvmeControllers[]</code>, <code>sataControllers[]</code>, <code>ideControllers[]</code>.</td>
			</tr>
			<tr>
					<td><code>advanced</code></td>
					<td><code>bootDiskCapacity</code>, <code>defaultVolumeProvisioningMode</code> (<code>Thin</code>, <code>Thick</code>, <code>ThickEagerZero</code>), <code>changeBlockTracking</code>.</td>
			</tr>
			<tr>
					<td><code>promoteDisksMode</code></td>
					<td><code>Online</code> (default), <code>Offline</code>, <code>Disabled</code>. See the gotchas.</td>
			</tr>
			<tr>
					<td><code>bootOptions</code></td>
					<td><code>firmware</code> (<code>bios</code>, <code>efi</code>: lower case, whatever the designer suggests), <code>efiSecureBoot</code>, <code>bootOrder</code>, <code>bootDelay</code>, <code>bootRetry</code>, <code>bootRetryDelay</code>, <code>networkBootProtocol</code>.</td>
			</tr>
			<tr>
					<td><code>readinessProbe</code></td>
					<td><code>tcpSocket.port</code>, <code>guestHeartbeat.thresholdStatus</code>, or <code>guestInfo[]</code>, with <code>periodSeconds</code> and <code>timeoutSeconds</code>.</td>
			</tr>
			<tr>
					<td><code>affinity</code></td>
					<td><code>vmAffinity</code> and <code>vmAntiAffinity</code>, each <code>requiredDuringSchedulingPreferredDuringExecution</code> (must hold) or <code>preferredDuringSchedulingPreferredDuringExecution</code> (best effort): a <code>labelSelector</code> and a <code>topologyKey</code>. Needs <code>groupName</code>.</td>
			</tr>
			<tr>
					<td><code>groupName</code></td>
					<td>The Virtual Machine Group the VM belongs to; the group then places it.</td>
			</tr>
			<tr>
					<td><code>crypto</code></td>
					<td><code>encryptionClassName</code>, <code>useDefaultKeyProvider</code> (default <code>true</code>), <code>vTPMMode</code>.</td>
			</tr>
			<tr>
					<td><code>minHardwareVersion</code></td>
					<td>A floor for the virtual hardware version: NVMe needs 14 or later.</td>
			</tr>
			<tr>
					<td><code>nextRestartTime</code></td>
					<td>Set to <code>now</code> to restart the VM, per <code>restartMode</code>.</td>
			</tr>
			<tr>
					<td><code>powerOffMode</code>, <code>suspendMode</code>, <code>restartMode</code></td>
					<td><code>TrySoft</code> (default), <code>Soft</code>, <code>Hard</code>.</td>
			</tr>
			<tr>
					<td><code>currentSnapshotName</code>, <code>policies[]</code>, <code>biosUUID</code>, <code>instanceUUID</code></td>
					<td>Revert to a snapshot, attach policies, pin identifiers.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (266)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.advanced</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{bootDiskCapacity: 40Gi, defaultVolumeProvisioningMode: Thin}</code></td>
					<td>Advanced describes a set of optional, advanced VM configuration options.</td>
			</tr>
			<tr>
					<td><code>spec.advanced.bootDiskCapacity</code></td>
					<td>int or string</td>
					<td></td>
					<td>e.g. <code>40Gi</code></td>
					<td>BootDiskCapacity is the capacity of the VM&rsquo;s boot disk &ndash; the first disk from the VirtualMachineImage from which the VM was deployed.</td>
			</tr>
			<tr>
					<td><code>spec.advanced.changeBlockTracking</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>ChangeBlockTracking is a flag that enables incremental backup support for this VM, a feature utilized by external backup systems such as VMware Data Recovery.</td>
			</tr>
			<tr>
					<td><code>spec.advanced.defaultVolumeProvisioningMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Thin</code>, <code>Thick</code>, <code>ThickEagerZero</code></td>
					<td>DefaultVolumeProvisioningMode specifies the default provisioning mode for persistent volumes managed by this VM.</td>
			</tr>
			<tr>
					<td><code>spec.affinity</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{vmAntiAffinity: {preferredDuringSchedulingPreferredDuringExecution: [{topologyKey: , ...}]}}</code></td>
					<td>Affinity describes the VM&rsquo;s scheduling constraints.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{preferredDuringSchedulingPreferredDuringExecution: [{labelSelector: {matchLabels: {, ...}}}]}</code></td>
					<td>VMAffinity describes affinity scheduling rules related to other VMs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{labelSelector: {matchLabels: {app: web}}, topologyKey: kubernetes.io/hostname}]</code></td>
					<td>PreferredDuringSchedulingPreferredDuringExecution describes affinity requirements that should be met, but the VM can still be scheduled if the requirement cannot be satisfied.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchLabels: {app: web}}</code></td>
					<td>LabelSelector is a label query over a set of VMs. When omitted, this term matches with no VMs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.preferredDuringSchedulingPreferredDuringExecution[].topologyKey</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>kubernetes.io/hostname</code></td>
					<td>TopologyKey describes where this VM should be co-located (affinity) or not co-located (anti-affinity).</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{labelSelector: {matchLabels: {app: web}}, topologyKey: kubernetes.io/hostname}]</code></td>
					<td>RequiredDuringSchedulingPreferredDuringExecution describes affinity requirements that must be met or the VM will not be scheduled.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchLabels: {app: web}}</code></td>
					<td>LabelSelector is a label query over a set of VMs. When omitted, this term matches with no VMs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAffinity.requiredDuringSchedulingPreferredDuringExecution[].topologyKey</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>kubernetes.io/hostname</code></td>
					<td>TopologyKey describes where this VM should be co-located (affinity) or not co-located (anti-affinity).</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{preferredDuringSchedulingPreferredDuringExecution: [{topologyKey: kubernetes.io/hos, ...}]}</code></td>
					<td>VMAntiAffinity describes anti-affinity scheduling rules related to other VMs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{topologyKey: kubernetes.io/hostname, labelSelector: {matchLabels: {app: web}}}]</code></td>
					<td>PreferredDuringSchedulingPreferredDuringExecution describes anti-affinity requirements that should be met, but the VM can still be scheduled if the requirement cannot be satisfied.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchLabels: {app: web}}</code></td>
					<td>LabelSelector is a label query over a set of VMs. When omitted, this term matches with no VMs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].labelSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.preferredDuringSchedulingPreferredDuringExecution[].topologyKey</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>kubernetes.io/hostname</code></td>
					<td>TopologyKey describes where this VM should be co-located (affinity) or not co-located (anti-affinity).</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{labelSelector: {matchLabels: {app: web}}, topologyKey: kubernetes.io/hostname}]</code></td>
					<td>RequiredDuringSchedulingPreferredDuringExecution describes anti-affinity requirements that must be met or the VM will not be scheduled.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchLabels: {app: web}}</code></td>
					<td>LabelSelector is a label query over a set of VMs. When omitted, this term matches with no VMs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[web]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].labelSelector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.affinity.vmAntiAffinity.requiredDuringSchedulingPreferredDuringExecution[].topologyKey</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>kubernetes.io/hostname</code></td>
					<td>TopologyKey describes where this VM should be co-located (affinity) or not co-located (anti-affinity).</td>
			</tr>
			<tr>
					<td><code>spec.biosUUID</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>4210d2a5-6d0e-4f6e-9c3a-0b1f2e3d4c5b</code></td>
					<td>BiosUUID describes the desired BIOS UUID for a VM. If omitted, this field defaults to a random UUID.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{bootDelay: 5s, bootOrder: [{name: &lt;device name&gt;, type: Disk}]}</code></td>
					<td>BootOptions describes the settings that control the boot behavior of the virtual machine. These settings take effect during the next power-on of the virtual machine.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.bootDelay</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>5s</code></td>
					<td>BootDelay is the delay before starting the boot sequence. The boot delay specifies a time interval between virtual machine power on or restart and the beginning of the boot sequence.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.bootOrder</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: &lt;device name&gt;, type: Disk}]</code></td>
					<td>BootOrder represents the boot order of the virtual machine. After list is exhausted, default BIOS boot device algorithm is used for booting.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.bootOrder[].name</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;device name&gt;</code></td>
					<td>Name represents the name of the bootable device. It is required for Disk and Network device types, while ignored for CDRom device types.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.bootOrder[].type</code></td>
					<td>string</td>
					<td>yes</td>
					<td><code>Disk</code>, <code>Network</code>, <code>CDRom</code></td>
					<td>Type represents the type of bootable device. The available device types are: - Disk - Network - CDRom</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.bootRetry</code></td>
					<td>string</td>
					<td></td>
					<td>default <code>Disabled</code></td>
					<td>BootRetry specifies whether a virtual machine that fails to boot will try again.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.bootRetryDelay</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10s</code></td>
					<td>BootRetryDelay specifies a time interval between virtual machine boot failure and the subsequent attempt to boot again.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.efiSecureBoot</code></td>
					<td>string</td>
					<td></td>
					<td><code>Enabled</code>, <code>Disabled</code>; default <code>Disabled</code></td>
					<td>EFISecureBoot specifies whether the virtual machine&rsquo;s firmware will perform signature checks of any EFI images loaded during startup.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.firmware</code></td>
					<td>string</td>
					<td></td>
					<td><code>bios</code>, <code>efi</code></td>
					<td>Firmware represents the firmware for the virtual machine to use. Any update to this value after the virtual machine has already been created will be ignored.</td>
			</tr>
			<tr>
					<td><code>spec.bootOptions.networkBootProtocol</code></td>
					<td>string</td>
					<td></td>
					<td><code>IP4</code>, <code>IP6</code>; default <code>IP4</code></td>
					<td>NetworkBootProtocol is the protocol to attempt during PXE network boot or NetBoot. The available protocols are: - IP4 &ndash; PXE (or Apple NetBoot) over IPv4.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cloudInit: {cloudConfig: {timezone: Europe/London, users: [{name: ops, ...}]}}}</code></td>
					<td>Bootstrap describes the desired state of the guest&rsquo;s bootstrap configuration. If omitted, a default bootstrap method may be selected based on the guest OS identifier.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cloudConfig: {timezone: Europe/London, users: [{name: ops, ...}]}}</code></td>
					<td>CloudInit may be used to bootstrap Linux guests with Cloud-Init or Windows guests that support Cloudbase-Init.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{timezone: Europe/London, users: [{name: ops, hashed_passwd: {key: ops-passwd, ...}}]}</code></td>
					<td>CloudConfig describes a subset of a Cloud-Init CloudConfig, used to bootstrap the VM.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.defaultUserEnabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>DefaultUserEnabled may be set to true to ensure even if the Users field is not empty, the default user is still created on systems that have one defined.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.runcmd</code></td>
					<td>any</td>
					<td></td>
					<td>e.g. <code>[systemctl enable --now nginx]</code></td>
					<td>RunCmd allows running one or more commands on the guest. The entries in this list can adhere to two, different formats: Format 1 &ndash; a string that contains the command and its arguments, ex.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.ssh_pwauth</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>SSHPwdAuth sets whether or not to accept password authentication. In order for this config to be applied, SSH may need to be restarted.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.timezone</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Europe/London</code></td>
					<td>Timezone describes the timezone represented in /usr/share/zoneinfo.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: ops, hashed_passwd: {key: ops-passwd, name: web-pw}}]</code></td>
					<td>Users allows adding/configuring one or more users on the guest.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].create_groups</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>CreateGroups is a flag that may be set to false to disable creation of specified user groups. Defaults to true when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].expiredate</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>2027-01-01</code></td>
					<td>ExpireData is the date on which the user&rsquo;s account will be disabled.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].gecos</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Operations user</code></td>
					<td>Gecos is an optional comment about the user, usually a comma-separated string of the user&rsquo;s real name and contact information.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].groups</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[sudo]</code></td>
					<td>Groups is an optional list of groups to add to the user.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].hashed_passwd</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: ops-passwd, name: web-pw}</code></td>
					<td>HashedPasswd is a hash of the user&rsquo;s password that will be applied even if the specified user already exists.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].hashed_passwd.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>ops-passwd</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].hashed_passwd.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>web-pw</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].homedir</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>/home/ops</code></td>
					<td>Homedir is the optional home directory for the user. Defaults to &ldquo;/home/<!-- raw HTML omitted -->&rdquo; when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].inactive</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>30</code></td>
					<td>Inactive optionally represents the number of days until the user is disabled.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].lock_passwd</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>false</code></td>
					<td>LockPasswd disables password login. Defaults to true when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>ops</code></td>
					<td>Name is the user&rsquo;s login name. When set to &ldquo;default&rdquo;, all other fields from this User must be nil.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].no_create_home</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>NoCreateHome prevents the creation of the home directory. Defaults to false when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].no_log_init</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>NoLogInit prevents the initialization of lastlog and faillog for the user. Defaults to false when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].no_user_group</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>NoUserGroup prevents the creation of the group named after the user. Defaults to false when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].passwd</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}</code></td>
					<td>Passwd is a hash of the user&rsquo;s password that will be applied only to a newly created user. To apply a new, hashed password to an existing user please use HashedPasswd instead.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].passwd.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;key in the Secret&gt;</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].passwd.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].primary_group</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ops</code></td>
					<td>PrimaryGroup is the primary group for the user. Defaults to the value of the Name field when it is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].selinux_user</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>staff_u</code></td>
					<td>SELinuxUser is the SELinux user for the user&rsquo;s login.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].shell</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>/bin/bash</code></td>
					<td>Shell is the path to the user&rsquo;s login shell.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].snapuser</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ops@example.com</code></td>
					<td>SnapUser specifies an e-mail address to create the user as a Snappy user through &ldquo;snap create-user&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].ssh_authorized_keys</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkJfr8Q3cNq ops@example]</code></td>
					<td>SSHAuthorizedKeys is a list of SSH keys to add to the user&rsquo;s authorized keys file.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].ssh_import_id</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[gh:octocat]</code></td>
					<td>SSHImportID is a list of SSH IDs to import for the user.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].ssh_redirect_user</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>SSHRedirectUser may be set to true to disable SSH logins for this user. Any SSH login as this user will timeout with a message to login instead as the default user.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].sudo</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ALL=(ALL) NOPASSWD:ALL</code></td>
					<td>Sudo is a sudo rule to apply to the user. When omitted, no sudo rules will be applied to the user.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].system</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>System is an optional flag that indicates the user should be created as a system user with no home directory. Defaults to false when Name is not &ldquo;default&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.users[].uid</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1001</code></td>
					<td>UID is the user&rsquo;s ID. When omitted the guest will default to the next available number.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{path: /etc/nginx/conf.d/lab.conf, content: server_tokens off;}]</code></td>
					<td>WriteFiles allows adding files to the guest file system.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].append</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Append specifies whether or not to append the content to an existing file if the file specified by Path already exists.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].content</code></td>
					<td>any</td>
					<td></td>
					<td>e.g. <code>server_tokens off;</code></td>
					<td>Content is the optional content to write to the provided Path. When omitted an empty file will be created or existing file will be modified.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].defer</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Defer indicates to defer writing the file until Cloud-Init&rsquo;s &ldquo;final&rdquo; stage, after users are created and packages are installed.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].encoding</code></td>
					<td>string</td>
					<td></td>
					<td><code>b64</code>, <code>base64</code>, <code>gz</code>, <code>gzip</code>, <code>gz+b64</code>, <code>gz+base64</code>, <code>gzip+b64</code>, <code>gzip+base64</code>, <code>text/plain</code>; default <code>text/plain</code></td>
					<td>Encoding is an optional encoding type of the content.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].owner</code></td>
					<td>string</td>
					<td></td>
					<td>default <code>root:root</code></td>
					<td>Owner is an optional &ldquo;owner:group&rdquo; to chown the file.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].path</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>/etc/nginx/conf.d/lab.conf</code></td>
					<td>Path is the path of the file to which the content is decoded and written.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.cloudConfig.write_files[].permissions</code></td>
					<td>string</td>
					<td></td>
					<td>default <code>0644</code></td>
					<td>Permissions an optional set of file permissions to set. &ldquo;0###&rdquo;. When omitted the guest will default this value to &ldquo;0644&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.instanceID</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>web-01-v2</code></td>
					<td>InstanceID is the cloud-init metadata instance ID. If omitted, this field defaults to the VM&rsquo;s BiosUUID.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.rawCloudConfig</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: user-data, name: jump-bootstrap}</code></td>
					<td>RawCloudConfig describes a key in a Secret resource that contains the CloudConfig data used to bootstrap the VM.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.rawCloudConfig.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>user-data</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.rawCloudConfig.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>jump-bootstrap</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.sshAuthorizedKeys</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[ssh-ed25519 AAAA... ops@admin]</code></td>
					<td>SSHAuthorizedKeys is a list of public keys that CloudInit will apply to the guest&rsquo;s default user.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.useGlobalNameserversAsDefault</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>UseGlobalNameserversAsDefault will use the global nameservers specified in the NetworkSpec as the per-interface nameservers when the per-interface nameservers is not provided.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.useGlobalSearchDomainsAsDefault</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>UseGlobalSearchDomainsAsDefault will use the global search domains specified in the NetworkSpec as the per-interface search domains when the per-interface search domains is not provided.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.waitOnNetwork4</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>WaitOnNetwork4 indicates whether the cloud-init datasource should wait for an IPv4 address to be available before writing the instance-data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.cloudInit.waitOnNetwork6</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>WaitOnNetwork6 indicates whether the cloud-init datasource should wait for an IPv6 address to be available before writing the instance-data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{password: {name: &lt;Secret name&gt;, key: password}, ...}</code></td>
					<td>LinuxPrep may be used to bootstrap Linux guests. The guest&rsquo;s networking stack is configured by Guest OS Customization (GOSC).</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.customizeAtNextPowerOn</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>CustomizeAtNextPowerOn describes when customization is performed on the VM. When set to false, the VM will not be customized at the next power on.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.expirePasswordAfterNextLogin</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>ExpirePasswordAfterNextLogin indicates whether or not the root account is required to change their password after the next login.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.hardwareClockIsUTC</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>HardwareClockIsUTC specifies whether the hardware clock is in UTC or local time.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.password</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{name: &lt;Secret name&gt;, key: password}</code></td>
					<td>Password is the new root password for the machine. When not explicitly specified, the Key field for the selector defaults to <code>password</code>.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.password.key</code></td>
					<td>string</td>
					<td></td>
					<td>default <code>password</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.password.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.scriptText</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{from: {key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}, value: '#!/bin/sh ...'}</code></td>
					<td>ScriptText is the script to run before and after customization. Please see <a href="https://knowledge.broadcom.com/external/article?legacyId=1026614">https://knowledge.broadcom.com/external/article?legacyId=1026614</a> for script examples.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.scriptText.from</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}</code></td>
					<td>From is specified to reference a value from a Secret resource.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.scriptText.from.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;key in the Secret&gt;</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.scriptText.from.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.scriptText.value</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>'#!/bin/sh ...'</code></td>
					<td>Value is used to directly specify a value.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.linuxPrep.timeZone</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Europe/London</code></td>
					<td>TimeZone is a case-sensitive timezone, such as Europe/Sofia. Valid values are based on the tz (timezone) database used by Linux and other Unix systems.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{rawSysprep: {key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}, ...}</code></td>
					<td>Sysprep may be used to bootstrap Windows guests. The guest&rsquo;s networking stack is configured by Guest OS Customization (GOSC).</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.customizeAtNextPowerOn</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>CustomizeAtNextPowerOn describes when customization is performed on the VM. When set to false, the VM will not be customized at the next power on.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.rawSysprep</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}</code></td>
					<td>RawSysprep describes a key in a Secret resource that contains an XML string of the Sysprep text used to bootstrap the VM.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.rawSysprep.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;key in the Secret&gt;</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.rawSysprep.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{guiRunOnce: {commands: [powershell -File C:\setup.ps1]}, ...}</code></td>
					<td>Sysprep is an object representation of a Windows sysprep.xml answer file. This field encloses all the individual keys listed in a sysprep.xml file.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.expirePasswordAfterNextLogin</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>ExpirePasswordAfterNextLogin indicates whether or not the local Administrators group accounts are required to change their password after the next login.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiRunOnce</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{commands: [powershell -File C:\setup.ps1]}</code></td>
					<td>GUIRunOnce is a representation of the Sysprep GuiRunOnce key.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiRunOnce.commands</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[powershell -File C:\setup.ps1]</code></td>
					<td>Commands is a list of commands to run at first user logon, after guest customization.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{autoLogonCount: 1, password: {name: &lt;Secret name&gt;, key: password}}</code></td>
					<td>GUIUnattended is a representation of the Sysprep GUIUnattended key.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended.autoLogon</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>AutoLogon determine whether the machine automatically logs on as Administrator.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended.autoLogonCount</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1</code></td>
					<td>AutoLogonCount specifies the number of times the machine should automatically log on as Administrator.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended.password</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{name: &lt;Secret name&gt;, key: password}</code></td>
					<td>Password is the new administrator password for the machine. To specify that the password should be set to blank (that is, no password), set the password value to NULL.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended.password.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>default <code>password</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended.password.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.guiUnattended.timeZone</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>85</code></td>
					<td>TimeZone is the time zone index for the virtual machine.ly/3Rzv8oL. Defaults to UTC.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{domainAdmin: svc-join@example.local, domainAdminPassword: {name: &lt;Secret name&gt;, ...}}</code></td>
					<td>Identification is a representation of the Sysprep Identification key.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification.domainAdmin</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>svc-join@example.local</code></td>
					<td>DomainAdmin is the domain user account used for authentication if the virtual machine is joining a domain.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification.domainAdminPassword</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{name: &lt;Secret name&gt;, key: domain_admin_password}</code></td>
					<td>DomainAdminPassword is the password for the domain user account used for authentication if the virtual machine is joining a domain.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification.domainAdminPassword.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>default <code>domain_admin_password</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification.domainAdminPassword.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification.domainOU</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>OU=Servers,DC=example,DC=local</code></td>
					<td>DomainOU is the MachineObjectOU which specifies the full LDAP path name of the OU to which the computer belongs.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.identification.joinWorkgroup</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>WORKGROUP</code></td>
					<td>JoinWorkgroup is the workgroup that the virtual machine should join.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.licenseFilePrintData</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{autoUsers: 5, autoMode: perSeat}</code></td>
					<td>LicenseFilePrintData is a representation of the Sysprep LicenseFilePrintData key.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.licenseFilePrintData.autoMode</code></td>
					<td>string</td>
					<td>yes</td>
					<td><code>perSeat</code>, <code>perServer</code></td>
					<td>AutoMode specifies the server licensing mode.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.licenseFilePrintData.autoUsers</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>5</code></td>
					<td>AutoUsers indicates the number of client licenses purchased for the VirtualCenter server being installed.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.scriptText</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{from: {key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}, ...}</code></td>
					<td>ScriptText describes the script to run before and after customization. The script must be a Windows batch file.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.scriptText.from</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}</code></td>
					<td>From is specified to reference a value from a Secret resource.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.scriptText.from.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;key in the Secret&gt;</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.scriptText.from.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.scriptText.value</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>powershell -File C:\setup.ps1</code></td>
					<td>Value is used to directly specify a value.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.userData</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{fullName: Lab Admin, orgName: Example Ltd}</code></td>
					<td>UserData is a representation of the Sysprep UserData key.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.userData.fullName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>Lab Admin</code></td>
					<td>FullName is the user&rsquo;s full name.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.userData.orgName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>Example Ltd</code></td>
					<td>OrgName is the name of the user&rsquo;s organization.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.userData.productID</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{name: &lt;Secret name&gt;, key: product_id}</code></td>
					<td>ProductID is a valid serial number. When not explicitly specified, the Key field for the selector defaults to <code>domain_admin_password</code>.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.userData.productID.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>default <code>product_id</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.sysprep.sysprep.userData.productID.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{properties: [{key: guestinfo.hostname, value: {from: {key: &lt;key in the Secret&gt;, ...}}}]}</code></td>
					<td>VAppConfig may be used to bootstrap guests that rely on vApp properties (how VMware surfaces OVF properties on guests) to transport data into the guest.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: guestinfo.hostname, value: {from: {key: &lt;key in the Secret&gt;, ...}}}]</code></td>
					<td>Properties is a list of vApp/OVF property key/value pairs.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>guestinfo.hostname</code></td>
					<td>Key is the key part of the key/value pair.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties[].value</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{from: {key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}, value: web-01}</code></td>
					<td>Value is the optional value part of the key/value pair.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties[].value.from</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: &lt;key in the Secret&gt;, name: &lt;Secret name&gt;}</code></td>
					<td>From is specified to reference a value from a Secret resource.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties[].value.from.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;key in the Secret&gt;</code></td>
					<td>Key is the key in the secret that specifies the requested data.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties[].value.from.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret name&gt;</code></td>
					<td>Name is the name of the secret.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.properties[].value.value</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>web-01</code></td>
					<td>Value is used to directly specify a value.</td>
			</tr>
			<tr>
					<td><code>spec.bootstrap.vAppConfig.rawProperties</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;ConfigMap with the OVF properties&gt;</code></td>
					<td>RawProperties is the name of a Secret resource in the same Namespace as this VM where each key/value pair from the Secret is used as a vApp key/value pair.</td>
			</tr>
			<tr>
					<td><code>spec.class</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{kind: VirtualMachineClass, name: best-effort-small}</code></td>
					<td>Class describes the VirtualMachineClassInstance resource that is referenced by this virtual machine.</td>
			</tr>
			<tr>
					<td><code>spec.class.apiVersion</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vmoperator.vmware.com/v1alpha5</code></td>
					<td>APIVersion defines the versioned schema of this representation of an object.</td>
			</tr>
			<tr>
					<td><code>spec.class.kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>VirtualMachineClass</code></td>
					<td>Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to.</td>
			</tr>
			<tr>
					<td><code>spec.class.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>best-effort-small</code></td>
					<td>Name refers to a unique resource in the current namespace.</td>
			</tr>
			<tr>
					<td><code>spec.className</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>best-effort-small</code></td>
					<td>ClassName describes the name of the VirtualMachineClass resource used to deploy this VM.</td>
			</tr>
			<tr>
					<td><code>spec.crypto</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{encryptionClassName: &lt;encryption class&gt;, useDefaultKeyProvider: true}</code></td>
					<td>Crypto describes the desired encryption state of the VirtualMachine.</td>
			</tr>
			<tr>
					<td><code>spec.crypto.encryptionClassName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;encryption class&gt;</code></td>
					<td>EncryptionClassName describes the name of the EncryptionClass resource used to encrypt this VM.</td>
			</tr>
			<tr>
					<td><code>spec.crypto.useDefaultKeyProvider</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>true</code></td>
					<td>UseDefaultKeyProvider describes the desired behavior for when an explicit EncryptionClass is not provided.</td>
			</tr>
			<tr>
					<td><code>spec.crypto.vTPMMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Clone</code>, <code>New</code>; default <code>New</code></td>
					<td>VTPMMode describes the desired behavior when deploying a VirtualMachine using a VirtualMachine-backed image which created from an encrypted VirtualMachine with a vTPM.</td>
			</tr>
			<tr>
					<td><code>spec.currentSnapshotName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>before-upgrade</code></td>
					<td>CurrentSnapshotName represents the desired snapshot that the VM should point to. This field can be specified to revert the VM to a given snapshot.</td>
			</tr>
			<tr>
					<td><code>spec.groupName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>web</code></td>
					<td>GroupName indicates the name of the VirtualMachineGroup to which this VM belongs. VMs that belong to a group do not drive their own placement, rather that is handled by the group.</td>
			</tr>
			<tr>
					<td><code>spec.guestID</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vmkernel9Guest</code></td>
					<td>GuestID describes the desired guest operating system identifier for a VM. The logic that determines the guest ID is as follows: If this field is set, then its value is used.</td>
			</tr>
			<tr>
					<td><code>spec.hardware</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cdrom: [{name: cdrom0, image: {kind: VirtualMachineImage, ...}}]}</code></td>
					<td>Hardware describes the VM&rsquo;s desired hardware.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: cdrom0, image: {kind: VirtualMachineImage, name: vmi-e400a813bbd5d52a5}}]</code></td>
					<td>Cdrom describes the desired state of the VM&rsquo;s CD-ROM devices. Each CD-ROM device requires a reference to an ISO-type VirtualMachineImage or ClusterVirtualMachineImage resource as backing.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].allowGuestControl</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>true</code></td>
					<td>AllowGuestControl describes whether or not a web console connection may be used to connect/disconnect the CD-ROM device.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].connected</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>true</code></td>
					<td>Connected describes the desired connection state of the CD-ROM device. When true, the CD-ROM device is added and connected to the VM.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].controllerBusNumber</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>0</code></td>
					<td>ControllerBusNumber describes the bus number of the controller to which this CD-ROM should be attached.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].controllerType</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>SATA</code></td>
					<td>ControllerType describes the type of the controller to which this CD-ROM should be attached.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].image</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{kind: VirtualMachineImage, name: vmi-e400a813bbd5d52a5}</code></td>
					<td>Image describes the reference to an ISO type VirtualMachineImage or ClusterVirtualMachineImage resource used as the backing for the CD-ROM.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].image.kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>VirtualMachineImage</code></td>
					<td>Kind describes the type of image, either a namespace-scoped VirtualMachineImage or cluster-scoped ClusterVirtualMachineImage.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].image.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vmi-e400a813bbd5d52a5</code></td>
					<td>Name refers to the name of a VirtualMachineImage resource in the same namespace as this VM or a cluster-scoped ClusterVirtualMachineImage.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>cdrom0</code></td>
					<td>Name consists of at least two lowercase letters or digits of this CD-ROM. It must be unique among all CD-ROM devices attached to the VM.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.cdrom[].unitNumber</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1</code></td>
					<td>UnitNumber describes the desired unit number for attaching the CD-ROM to a storage controller. When omitted, the next available unit number of the selected controller is used.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.ideControllers</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{busNumber: 0}]</code></td>
					<td>IDEControllers describes the desired list of IDE controllers for the VM. Defaults to two IDE controllers, with bus 0 and bus 1.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.ideControllers[].busNumber</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>0</code></td>
					<td>BusNumber describes the desired bus number of the controller.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.nvmeControllers</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{busNumber: 0, sharingMode: None}]</code></td>
					<td>NVMEControllers describes the desired list of NVME controllers for the VM.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.nvmeControllers[].busNumber</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>0</code></td>
					<td>BusNumber describes the desired bus number of the controller.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.nvmeControllers[].sharingMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>None</code>, <code>Physical</code>; default <code>None</code></td>
					<td>SharingMode describes the sharing mode for the controller. Defaults to None.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.sataControllers</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{busNumber: 0}]</code></td>
					<td>SATAControllers describes the desired list of SATA controllers for the VM.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.sataControllers[].busNumber</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>0</code></td>
					<td>BusNumber describes the desired bus number of the controller.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.scsiControllers</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{busNumber: 0, type: ParaVirtual}]</code></td>
					<td>SCSIControllers describes the desired list of SCSI controllers for the VM.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.scsiControllers[].busNumber</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>0</code></td>
					<td>BusNumber describes the desired bus number of the controller.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.scsiControllers[].sharingMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>None</code>, <code>Physical</code>, <code>Virtual</code>; default <code>None</code></td>
					<td>SharingMode describes the sharing mode for the controller. Defaults to None.</td>
			</tr>
			<tr>
					<td><code>spec.hardware.scsiControllers[].type</code></td>
					<td>string</td>
					<td></td>
					<td><code>ParaVirtual</code>, <code>BusLogic</code>, <code>LsiLogic</code>, <code>LsiLogicSAS</code>; default <code>ParaVirtual</code></td>
					<td>Type describes the desired type of SCSI controller. Defaults to ParaVirtual.</td>
			</tr>
			<tr>
					<td><code>spec.image</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{kind: VirtualMachineImage, name: vmi-0123456789abcdef0}</code></td>
					<td>Image describes the reference to the VirtualMachineImage or ClusterVirtualMachineImage resource used to deploy this VM.imageName, the value of spec.image.name MUST be a Kubernetes object &hellip;</td>
			</tr>
			<tr>
					<td><code>spec.image.kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>VirtualMachineImage</code></td>
					<td>Kind describes the type of image, either a namespace-scoped VirtualMachineImage or cluster-scoped ClusterVirtualMachineImage.</td>
			</tr>
			<tr>
					<td><code>spec.image.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vmi-0123456789abcdef0</code></td>
					<td>Name refers to the name of a VirtualMachineImage resource in the same namespace as this VM or a cluster-scoped ClusterVirtualMachineImage.</td>
			</tr>
			<tr>
					<td><code>spec.imageName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ubuntu-24.04-server-cloudimg-amd64</code></td>
					<td>ImageName describes the name of the image resource used to deploy this VM. This field may be used to specify the name of a VirtualMachineImage or ClusterVirtualMachineImage resource.</td>
			</tr>
			<tr>
					<td><code>spec.instanceUUID</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>5010c9b4-1f2e-4d3c-8b7a-6e5f4d3c2b1a</code></td>
					<td>InstanceUUID describes the desired Instance UUID for a VM. If omitted, this field defaults to a random UUID. This value is only used for the VM Instance UUID, it is not used within cloudInit.</td>
			</tr>
			<tr>
					<td><code>spec.minHardwareVersion</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>21</code></td>
					<td>MinHardwareVersion describes the desired, minimum hardware version. The logic that determines the hardware version is as follows: 1.</td>
			</tr>
			<tr>
					<td><code>spec.network</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{hostName: dc01, interfaces: [{name: eth0, addresses: [172.30.0.34/27]}]}</code></td>
					<td>Network describes the desired network configuration for the VM.</td>
			</tr>
			<tr>
					<td><code>spec.network.disabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Disabled is a flag that indicates whether or not to disable networking for this VM.</td>
			</tr>
			<tr>
					<td><code>spec.network.domainName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>lab.local</code></td>
					<td>DomainName describes the value the guest uses as its domain name.</td>
			</tr>
			<tr>
					<td><code>spec.network.hostName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>dc01</code></td>
					<td>HostName describes the value the guest uses as its host name. If omitted, the name of the VM will be used.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: eth0, addresses: [172.30.0.34/27]}]</code></td>
					<td>Interfaces is the list of network interfaces used by this VM. If the Interfaces field is empty and the Disabled field is false, then a default interface with the name eth0 will be created.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].addresses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[172.30.0.34/27]</code></td>
					<td>Addresses is an optional list of IP4 or IP6 addresses to assign to this interface. 192.168.0.10/24 or 2001:db8:101::a/64.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].dhcp4</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>DHCP4 indicates whether or not this interface uses DHCP for IP4 networking.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].dhcp6</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>DHCP6 indicates whether or not this interface uses DHCP for IP6 networking.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].gateway4</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>172.30.0.33</code></td>
					<td>Gateway4 is the default, IP4 gateway for this interface. If unset, the gateway from the network provider will be used.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].gateway6</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>fd00::1</code></td>
					<td>Gateway6 is the primary IP6 gateway for this interface. If unset, the gateway from the network provider will be used.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].guestDeviceName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>eth0</code></td>
					<td>GuestDeviceName is used to rename the device inside the guest when the bootstrap provider is Cloud-Init. dvd, cdrom, sda, etc.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].macAddr</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>00:50:56:00:00:10</code></td>
					<td>MACAddr is the optional MAC address of this interface. If no MAC address is provided, one will be generated by either the network provider or vCenter.nsx.vmware.com.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].mtu</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1500</code></td>
					<td>MTU is the Maximum Transmission Unit size in bytes.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>eth0</code></td>
					<td>Name describes the unique name of this network interface, used to distinguish it from other network interfaces attached to this VM.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].nameservers</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[172.30.0.34]</code></td>
					<td>Nameservers is a list of IP4 and/or IP6 addresses used as DNS nameservers.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].network</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{kind: Subnet, name: sn-mgmt}</code></td>
					<td>Network is the name of the network resource to which this interface is connected. If no network is provided, then this interface will be connected to the Namespace&rsquo;s default network.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].network.apiVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>crd.nsx.vmware.com/v1alpha1</code></td>
					<td>APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].network.kind</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Subnet</code></td>
					<td>Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].network.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>sn-mgmt</code></td>
					<td>Name refers to a unique resource in the current namespace.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].routes</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{to: 172.16.0.0/16, via: 10.200.0.1}]</code></td>
					<td>Routes is a list of optional, static routes.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].routes[].metric</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>100</code></td>
					<td>Metric is the weight/priority of the route.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].routes[].to</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>172.16.0.0/16</code></td>
					<td>To is either &ldquo;default&rdquo;, or an IP4 or IP6 address.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].routes[].via</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>10.200.0.1</code></td>
					<td>Via is an IP4 or IP6 address.</td>
			</tr>
			<tr>
					<td><code>spec.network.interfaces[].searchDomains</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[lab.local]</code></td>
					<td>SearchDomains is a list of search domains used when resolving IP addresses with DNS.</td>
			</tr>
			<tr>
					<td><code>spec.network.nameservers</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.2]</code></td>
					<td>Nameservers is a list of IP4 and/or IP6 addresses used as DNS nameservers. These are applied globally. The Cloud-Init bootstrap provider supports per-interface nameservers.</td>
			</tr>
			<tr>
					<td><code>spec.network.searchDomains</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[lab.local]</code></td>
					<td>SearchDomains is a list of search domains used when resolving IP addresses with DNS. These are applied globally. The Cloud-Init bootstrap provider supports per-interface search domains.</td>
			</tr>
			<tr>
					<td><code>spec.nextRestartTime</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>now</code></td>
					<td>NextRestartTime may be used to restart the VM, in accordance with RestartMode, by setting the value of this field to &ldquo;now&rdquo; (case-insensitive).</td>
			</tr>
			<tr>
					<td><code>spec.policies</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{kind: ComputePolicy, name: &lt;compute policy&gt;}]</code></td>
					<td>Policies describes a list of policies that should be explicitly applied to this VM. Please consult a policy to determine if it may be applied directly.</td>
			</tr>
			<tr>
					<td><code>spec.policies[].apiVersion</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vsphere.policy.vmware.com/v1alpha1</code></td>
					<td>APIVersion defines the versioned schema of this representation of an object.</td>
			</tr>
			<tr>
					<td><code>spec.policies[].kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>ComputePolicy</code></td>
					<td>Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to.</td>
			</tr>
			<tr>
					<td><code>spec.policies[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;compute policy&gt;</code></td>
					<td>Name refers to a unique resource in the current namespace.</td>
			</tr>
			<tr>
					<td><code>spec.powerOffMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Hard</code>, <code>Soft</code>, <code>TrySoft</code>; default <code>TrySoft</code></td>
					<td>PowerOffMode describes the desired behavior when powering off a VM. There are three, supported power off modes: Hard, Soft, and TrySoft.</td>
			</tr>
			<tr>
					<td><code>spec.powerState</code></td>
					<td>string</td>
					<td></td>
					<td><code>PoweredOff</code>, <code>PoweredOn</code>, <code>Suspended</code></td>
					<td>PowerState describes the desired power state of a VirtualMachine.&quot; However, once the field is set to a non-empty value, it may no longer be set to an empty value.</td>
			</tr>
			<tr>
					<td><code>spec.promoteDisksMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Online</code>, <code>Offline</code>, <code>Disabled</code>; default <code>Online</code></td>
					<td>PromoteDisksMode describes the mode used to promote a VM&rsquo;s delta disks to full disks. The available modes are: - Disabled &ndash; Do not promote disks.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{guestInfo: [{key: guestinfo.ready, value: &quot;true&quot;}], tcpSocket: {host: 10.200.0.10, ...}}</code></td>
					<td>ReadinessProbe describes a probe used to determine the VM&rsquo;s ready state.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.guestHeartbeat</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{thresholdStatus: green}</code></td>
					<td>GuestHeartbeat specifies an action involving the guest heartbeat status.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.guestHeartbeat.thresholdStatus</code></td>
					<td>string</td>
					<td></td>
					<td><code>yellow</code>, <code>green</code>; default <code>green</code></td>
					<td>ThresholdStatus is the value that the guest heartbeat status must be at or above to be considered successful.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.guestInfo</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: guestinfo.ready, value: &quot;true&quot;}]</code></td>
					<td>GuestInfo specifies an action involving key/value pairs from GuestInfo.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.guestInfo[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>guestinfo.ready</code></td>
					<td>Key is the name of the GuestInfo key. The key is automatically prefixed with &ldquo;guestinfo.&rdquo; before being evaluated.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.guestInfo[].value</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&quot;true&quot;</code></td>
					<td>Value is a regular expression that is matched against the value of the specified key. An empty value is the equivalent of &ldquo;match any&rdquo; or &ldquo;.*&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.periodSeconds</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>10</code></td>
					<td>PeriodSeconds specifics how often (in seconds) to perform the probe. Defaults to 10 seconds. Minimum value is 1.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.tcpSocket</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{host: 10.200.0.10, port: 22}</code></td>
					<td>TCPSocket specifies an action involving a TCP port. Deprecated: The TCPSocket action requires network connectivity that is not supported in all environments.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.tcpSocket.host</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10.200.0.10</code></td>
					<td>Host is an optional host name to connect to. Host defaults to the VM IP.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.tcpSocket.port</code></td>
					<td>int or string</td>
					<td>yes</td>
					<td>e.g. <code>22</code></td>
					<td>Port specifies a number or name of the port to access on the VM. If the format of port is a number, it must be in the range 1 to 65535.</td>
			</tr>
			<tr>
					<td><code>spec.readinessProbe.timeoutSeconds</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>10</code></td>
					<td>TimeoutSeconds specifies a number of seconds after which the probe times out. Defaults to 10 seconds. Minimum value is 1.</td>
			</tr>
			<tr>
					<td><code>spec.reserved</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{resourcePolicyName: &lt;resource policy&gt;}</code></td>
					<td>Reserved describes a set of VM configuration options reserved for system use.</td>
			</tr>
			<tr>
					<td><code>spec.reserved.resourcePolicyName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;resource policy&gt;</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>spec.restartMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Hard</code>, <code>Soft</code>, <code>TrySoft</code>; default <code>TrySoft</code></td>
					<td>RestartMode describes the desired behavior for restarting a VM when spec.nextRestartTime is set to &ldquo;now&rdquo; (case-insensitive).</td>
			</tr>
			<tr>
					<td><code>spec.storageClass</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vsan-default-storage-policy</code></td>
					<td>StorageClass describes the name of a Kubernetes StorageClass resource used to configure this VM&rsquo;s storage-related attributes.</td>
			</tr>
			<tr>
					<td><code>spec.suspendMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Hard</code>, <code>Soft</code>, <code>TrySoft</code>; default <code>TrySoft</code></td>
					<td>SuspendMode describes the desired behavior when suspending a VM. There are three, supported suspend modes: Hard, Soft, and TrySoft.</td>
			</tr>
			<tr>
					<td><code>spec.volumes</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: data, persistentVolumeClaim: {claimName: web-01-data, ...}}]</code></td>
					<td>Volumes describes a list of volumes that can be mounted to the VM.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].applicationType</code></td>
					<td>string</td>
					<td></td>
					<td><code>OracleRAC</code>, <code>MicrosoftWSFC</code></td>
					<td>ApplicationType describes the type of application for which this volume is intended to be used.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].controllerBusNumber</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>0</code></td>
					<td>ControllerBusNumber describes the bus number of the controller to which this volume should be attached.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].controllerType</code></td>
					<td>string</td>
					<td></td>
					<td><code>IDE</code>, <code>NVME</code>, <code>SCSI</code>, <code>SATA</code></td>
					<td>ControllerType describes the type of the controller to which this volume should be attached.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].diskMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>IndependentNonPersistent</code>, <code>IndependentPersistent</code>, <code>NonPersistent</code>, <code>Persistent</code></td>
					<td>DiskMode describes the desired mode to use when attaching the volume.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>data</code></td>
					<td>Name represents the volume&rsquo;s name. Must be a DNS_LABEL and unique within the VM.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].persistentVolumeClaim</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{claimName: web-01-data, instanceVolumeClaim: {size: 50Gi, ...}}</code></td>
					<td>PersistentVolumeClaim represents a reference to a PersistentVolumeClaim in the same namespace.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].persistentVolumeClaim.claimName</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>web-01-data</code></td>
					<td>claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].persistentVolumeClaim.instanceVolumeClaim</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{size: 50Gi, storageClass: vsan-default-storage-policy}</code></td>
					<td>InstanceVolumeClaim is set if the PVC is backed by instance storage.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].persistentVolumeClaim.instanceVolumeClaim.size</code></td>
					<td>int or string</td>
					<td>yes</td>
					<td>e.g. <code>50Gi</code></td>
					<td>Size is the size of the requested instance storage volume.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].persistentVolumeClaim.instanceVolumeClaim.storageClass</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vsan-default-storage-policy</code></td>
					<td>StorageClass is the name of the Kubernetes StorageClass that provides the backing storage for this instance storage volume.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].persistentVolumeClaim.readOnly</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>readOnly Will force the ReadOnly setting in VolumeMounts. Default false.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].removable</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>true</code></td>
					<td>Removable describes whether or not this volume may be removed from spec.volumes.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].sharingMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>MultiWriter</code>, <code>None</code></td>
					<td>SharingMode describes the volume&rsquo;s desired sharing mode. When applicationType=OracleRAC, this field defaults to MultiWriter.</td>
			</tr>
			<tr>
					<td><code>spec.volumes[].unitNumber</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1</code></td>
					<td>UnitNumber describes the desired unit number for attaching the volume to a storage controller. When omitted, the next available unit number of the selected controller is used.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Minimal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vm1</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vmoperator.vmware.com/v1alpha5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachine</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-01</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">labels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">web}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">className</span><span class="p">:</span><span class="w"> </span><span class="l">best-effort-small</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">imageName</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-24.04-server-cloudimg-amd64</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">storageClass</span><span class="p">:</span><span class="w"> </span><span class="l">vsan-default-storage-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">wait</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">conditions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachineGuestNetworkConfigSynced</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;True&#34;</span><span class="w">
</span></span></span></code></pre></div><p><strong>Recipes</strong></p>
<ul>
<li>
<p><em>A Linux user with an SSH key and a password</em>, inline cloud-init. The
password is <strong>not</strong> a string here: <code>passwd</code> and <code>hashed_passwd</code> reference
a key in a Secret, and a plain string fails with <code>cannot restore struct from: string</code>. The hash can come from <a href="#utilpasswordentry">Util.PasswordEntry</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cloudInit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">cloudConfig</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">users</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ops</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">sudo</span><span class="p">:</span><span class="w"> </span><span class="l">ALL=(ALL) NOPASSWD:ALL</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">lock_passwd</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">hashed_passwd</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-pw         </span><span class="w"> </span><span class="c"># a Secret in the namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">ops-passwd      </span><span class="w"> </span><span class="c"># holding ${resource.pw.sha512crypt}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">ssh_authorized_keys</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span>- <span class="l">ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOkJfr8Q3cNq ops@example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">runcmd</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="p">[</span><span class="l">systemctl, enable, --now, ssh]</span><span class="w">
</span></span></span></code></pre></div><p>We logged in through a load balancer as <code>ops</code> with that key; <code>sudo</code> needed
no password and the shadow entry held the <code>$6$</code> hash.</p>
</li>
<li>
<p><em>Windows, or a long first-boot script:</em> keep the whole cloud-config in a
Secret and point <code>rawCloudConfig</code> at it. Our jump hosts run cloudbase-init
this way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cloudInit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">rawCloudConfig</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">jump-bootstrap    </span><span class="w"> </span><span class="c"># the Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">user-data          </span><span class="w"> </span><span class="c"># the key inside it</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><em>A static address on a VPC subnet.</em> With cloud-init the DNS servers go on
the interface:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">network</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">hostName</span><span class="p">:</span><span class="w"> </span><span class="l">dc01</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">interfaces</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">eth0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">network</span><span class="p">:</span><span class="w"> </span>{<span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="nt">crd.nsx.vmware.com/v1alpha1, kind</span><span class="p">:</span><span class="w"> </span><span class="nt">Subnet, name</span><span class="p">:</span><span class="w"> </span><span class="l">sn-mgmt}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">addresses</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;172.30.0.34/27&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">gateway4</span><span class="p">:</span><span class="w"> </span><span class="m">172.30.0.33</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">nameservers</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">172.30.0.34</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cloudInit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">rawCloudConfig</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">dc-bootstrap, key</span><span class="p">:</span><span class="w"> </span><span class="l">user-data}</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><em>An address on a subnet without choosing it:</em> name the subnet and leave
<code>addresses</code> out. VM Operator takes one from the subnet and configures the
guest; ours got <code>172.30.0.2</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">network</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">interfaces</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">eth0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">network</span><span class="p">:</span><span class="w"> </span>{<span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="nt">crd.nsx.vmware.com/v1alpha1, kind</span><span class="p">:</span><span class="w"> </span><span class="nt">Subnet, name</span><span class="p">:</span><span class="w"> </span><span class="l">sn-app}</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><em>An extra data disk:</em> a Persistent Volume Claim in the same blueprint, then
the VM below. It arrived in the guest as <code>sdb</code>, 5 GiB, beside the image&rsquo;s
10 GiB <code>sda</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">persistentVolumeClaim</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">claimName</span><span class="p">:</span><span class="w"> </span><span class="l">web-01-data</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><em>An ISO in the CD-ROM</em>, for an installer (our nested hosts boot the ESXi
installer this way). <code>guestID</code> becomes mandatory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">guestID</span><span class="p">:</span><span class="w"> </span><span class="l">vmkernel9Guest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">hardware</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cdrom</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">cdrom0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span>{<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="nt">VirtualMachineImage, name</span><span class="p">:</span><span class="w"> </span><span class="l">vmi-e400a813bbd5d52a5}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">connected</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">allowGuestControl</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div></li>
<li>
<p><em>A bigger boot disk than the image&rsquo;s:</em> <code>advanced.bootDiskCapacity: 80Gi</code>.
The guest still has to grow its partition.</p>
</li>
<li>
<p><em>Keep VMs apart.</em> Affinity rules only work for members of a
<a href="#virtual-machine-group">Virtual Machine Group</a>; on a VM without
<code>groupName</code> the Supervisor refuses them (<code>spec.groupName: Required value: when setting affinity</code>). Our two web VMs with this rule landed on
different hosts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">groupName</span><span class="p">:</span><span class="w"> </span><span class="l">web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">affinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">vmAntiAffinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">preferredDuringSchedulingPreferredDuringExecution</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">labelSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w"> </span>{<span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">web}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">topologyKey</span><span class="p">:</span><span class="w"> </span><span class="l">kubernetes.io/hostname</span><span class="w">
</span></span></span></code></pre></div></li>
</ul>
<p><strong>Status worth reading:</strong> <code>status.network.primaryIP4</code> (after the wait above),
<code>status.powerState</code>, <code>status.nodeName</code> (the ESXi host), <code>status.zone</code>,
<code>status.instanceUUID</code>, <code>status.biosUUID</code>, <code>status.hardwareVersion</code>, and
<code>status.volumes[]</code> with <code>attached</code> per disk.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>DNS settings depend on the bootstrap provider: <code>spec.network.nameservers</code>
works only with LinuxPrep and Sysprep, the per-interface <code>nameservers</code> only
with CloudInit and Sysprep, and a VM with no bootstrap can have neither.
The Supervisor says which: <code>nameservers is available only with the following bootstrap providers: ...</code>.</li>
<li><code>promoteDisksMode</code> defaults to <code>Online</code>: after a fast deploy from a linked
clone, VM Operator copies the disks into full disks while the VM runs. For
a large image that is real I/O; <code>Disabled</code> keeps the linked clone and
deploys faster. Our Windows jump host was ready in 15.1 minutes with
<code>Disabled</code> against 17.6 with the default. VMs with snapshots cannot
promote online.</li>
<li>The first deployment of a new image into a VPC can fail with an NSX
<code>503638</code> error while the image is still being cached; a retry succeeds.</li>
<li>Changing <code>className</code> resizes the VM; changing <code>manifest</code> in a new blueprint
version recreates it.</li>
</ul>
<h2 id="virtual-machine-group">Virtual Machine Group</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: vmoperator.vmware.com/v1alpha5</code>,
<code>kind: VirtualMachineGroup</code>. A set of VMs placed and powered as one: a boot
order with delays between steps, and one power state for all of them.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>bootOrder[]</code></td>
					<td>Steps, in order. Each has <code>members[]</code> (<code>kind</code>: <code>VirtualMachine</code> default or <code>VirtualMachineGroup</code>; <code>name</code>) and <code>powerOnDelay</code> before the step.</td>
			</tr>
			<tr>
					<td><code>powerState</code></td>
					<td><code>PoweredOn</code> (default), <code>PoweredOff</code>, <code>Suspended</code>, applied to every member.</td>
			</tr>
			<tr>
					<td><code>powerOffMode</code>, <code>suspendMode</code></td>
					<td><code>TrySoft</code> (default), <code>Soft</code>, <code>Hard</code>.</td>
			</tr>
			<tr>
					<td><code>groupName</code></td>
					<td>A parent group, to nest groups.</td>
			</tr>
			<tr>
					<td><code>nextForcePowerStateSyncTime</code></td>
					<td><code>now</code> pushes the group&rsquo;s power state to every member again.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (10)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.bootOrder</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{members: [{name: web-01, kind: VirtualMachine}], powerOnDelay: 30s}]</code></td>
					<td>BootOrder describes the boot sequence for this group members. Each boot order contains a set of members that will be powered on simultaneously, with an optional delay before powering on.</td>
			</tr>
			<tr>
					<td><code>spec.bootOrder[].members</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: web-01, kind: VirtualMachine}]</code></td>
					<td>Members describes the names of VirtualMachine or VirtualMachineGroup objects that are members of this boot order group.</td>
			</tr>
			<tr>
					<td><code>spec.bootOrder[].members[].kind</code></td>
					<td>string</td>
					<td></td>
					<td><code>VirtualMachine</code>, <code>VirtualMachineGroup</code>; default <code>VirtualMachine</code></td>
					<td>Kind is the kind of member of this group, which can be either VirtualMachine or VirtualMachineGroup. If omitted, it defaults to VirtualMachine.</td>
			</tr>
			<tr>
					<td><code>spec.bootOrder[].members[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>web-01</code></td>
					<td>Name is the name of member of this group.</td>
			</tr>
			<tr>
					<td><code>spec.bootOrder[].powerOnDelay</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>30s</code></td>
					<td>PowerOnDelay is the amount of time to wait before powering on all the members of this boot order group.</td>
			</tr>
			<tr>
					<td><code>spec.groupName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;parent group&gt;</code></td>
					<td>GroupName describes the name of the group that this group belongs to.</td>
			</tr>
			<tr>
					<td><code>spec.nextForcePowerStateSyncTime</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>now</code></td>
					<td>NextForcePowerStateSyncTime may be used to force sync the power state of the group to all of its members, by setting the value of this field to &ldquo;now&rdquo; (case-insensitive).</td>
			</tr>
			<tr>
					<td><code>spec.powerOffMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Hard</code>, <code>Soft</code>, <code>TrySoft</code></td>
					<td>PowerOffMode describes the desired behavior when powering off a VM Group. Refer to the VirtualMachine.PowerOffMode field for more details.</td>
			</tr>
			<tr>
					<td><code>spec.powerState</code></td>
					<td>string</td>
					<td></td>
					<td><code>PoweredOff</code>, <code>PoweredOn</code>, <code>Suspended</code></td>
					<td>PowerState describes the desired power state of a VirtualMachineGroup.</td>
			</tr>
			<tr>
					<td><code>spec.suspendMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Hard</code>, <code>Soft</code>, <code>TrySoft</code></td>
					<td>SuspendMode describes the desired behavior when suspending a VM Group. Refer to the VirtualMachine.SuspendMode field for more details.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Recipe, one VM before the next, thirty seconds apart:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">appGroup</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vmoperator.vmware.com/v1alpha5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachineGroup</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">app}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">bootOrder</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">members</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-01}]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">members</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-02}]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">powerOnDelay</span><span class="p">:</span><span class="w"> </span><span class="l">30s</span><span class="w">
</span></span></span></code></pre></div><p>Each member names the group in <code>spec.groupName: app</code> and depends on the group
resource (<code>dependsOn: [appGroup]</code>), because the group is referenced only by
name.</p>
<p><strong>Status worth reading:</strong> <code>status.members[]</code> (each member&rsquo;s power state and
placement), <code>status.conditions</code>.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>A member of a later step stays off until every member of the earlier steps
is on. When our first test&rsquo;s <code>web-01</code> failed to create, <code>web-02</code> sat
powered off for good.</li>
<li>A VM in a group doesn&rsquo;t place itself; the group places its members.</li>
</ul>
<h2 id="virtual-machine-service">Virtual Machine Service</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: vmoperator.vmware.com/v1alpha5</code>,
<code>kind: VirtualMachineService</code>. A Kubernetes-style service in front of VMs,
selected by label. With <code>type: LoadBalancer</code>, the VPC&rsquo;s load balancer gives
it an external address. That&rsquo;s how a VM on a private subnet is reached from
outside.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>type</code></td>
					<td><strong>Required.</strong> <code>LoadBalancer</code> or <code>ClusterIP</code>. The API lists <code>ExternalName</code> too; the VM Operator documentation says it isn&rsquo;t supported.</td>
			</tr>
			<tr>
					<td><code>selector</code></td>
					<td>Labels of the VMs behind it.</td>
			</tr>
			<tr>
					<td><code>ports[]</code></td>
					<td><code>name</code>, <code>port</code>, <code>targetPort</code>, <code>protocol</code> (<code>TCP</code>, <code>UDP</code>, <code>SCTP</code>).</td>
			</tr>
			<tr>
					<td><code>loadBalancerSourceRanges[]</code></td>
					<td>Source CIDRs allowed to reach a <code>LoadBalancer</code> service.</td>
			</tr>
			<tr>
					<td><code>loadBalancerIP</code>, <code>clusterIp</code>, <code>externalName</code></td>
					<td>A requested address, a fixed cluster IP, a DNS name.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (11)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.clusterIp</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10.96.0.20</code></td>
					<td>ClusterIP is the IP address of the service and is usually assigned randomly by the master.</td>
			</tr>
			<tr>
					<td><code>spec.externalName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>web.example.com</code></td>
					<td>ExternalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved.</td>
			</tr>
			<tr>
					<td><code>spec.loadBalancerIP</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>192.168.144.20</code></td>
					<td>LoadBalancer will get created with the IP specified in this field.</td>
			</tr>
			<tr>
					<td><code>spec.loadBalancerSourceRanges</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.0.0.0/8]</code></td>
					<td>LoadBalancerSourceRanges is an array of IP addresses in the format of CIDRs, for example: 103.21.244.0/22 and 10.0.0.0/24.</td>
			</tr>
			<tr>
					<td><code>spec.ports</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: rdp, port: 3389}]</code></td>
					<td>Ports specifies a list of VirtualMachineServicePort to expose with this VirtualMachineService. Each of these ports will be an accessible network entry point to access this service by.</td>
			</tr>
			<tr>
					<td><code>spec.ports[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>rdp</code></td>
					<td>Name describes the name to be used to identify this VirtualMachineServicePort.</td>
			</tr>
			<tr>
					<td><code>spec.ports[].port</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>3389</code></td>
					<td>Port describes the external port that will be exposed by the service.</td>
			</tr>
			<tr>
					<td><code>spec.ports[].protocol</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>TCP</code></td>
					<td>Protocol describes the Layer 4 transport protocol for this port. Supports &ldquo;TCP&rdquo;, &ldquo;UDP&rdquo;, and &ldquo;SCTP&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.ports[].targetPort</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>3389</code></td>
					<td>TargetPort describes the internal port open on a VirtualMachine that should be mapped to the external Port.</td>
			</tr>
			<tr>
					<td><code>spec.selector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>Selector specifies a map of key-value pairs, also known as a Label Selector, that is used to match this VirtualMachineService with the set of VirtualMachines that should back this VirtualMachineService.</td>
			</tr>
			<tr>
					<td><code>spec.type</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>LoadBalancer</code></td>
					<td>Type specifies a desired VirtualMachineServiceType for this VirtualMachineService. Supported types are ClusterIP, LoadBalancer, ExternalName.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Recipe, RDP to a jump host, the only way into our labs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">jumpAccess</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vmoperator.vmware.com/v1alpha5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachineService</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">jump-access}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">LoadBalancer</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">selector</span><span class="p">:</span><span class="w"> </span>{<span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">jump}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">rdp, port</span><span class="p">:</span><span class="w"> </span><span class="nt">3389, targetPort</span><span class="p">:</span><span class="w"> </span><span class="nt">3389, protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">loadBalancerSourceRanges</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">10.0.0.0</span><span class="l">/8]</span><span class="w">
</span></span></span></code></pre></div><p><strong>Status worth reading:</strong> <code>status.loadBalancer.ingress[0].ip</code>, the external
address.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li><code>LoadBalancer</code> needs the VPC&rsquo;s load balancer. VCF Automation waits for the
address by itself, so in a VPC without one the request stays in progress
until it times out. A VPC made by a blueprint never has one; see <a href="#vpc">VPC</a>.</li>
<li>A service with several VMs behind it spreads connections across them: our
SSH sessions alternated between the two web VMs.</li>
<li>The palette writes <code>v1alpha3</code>; we use <code>v1alpha5</code>, the Supervisor&rsquo;s stored
version. Both are served.</li>
</ul>
<h2 id="subnet">Subnet</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: crd.nsx.vmware.com/v1alpha1</code>,
<code>kind: Subnet</code>. A subnet of the namespace&rsquo;s VPC: a layer-2 segment with its
own address range, for VMs that need a network of their own.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>accessMode</code></td>
					<td><code>Private</code> (default): routed inside the VPC only. <code>PrivateTGW</code>: reachable from other VPCs through the transit gateway. <code>Public</code>: from the external network.</td>
			</tr>
			<tr>
					<td><code>ipv4SubnetSize</code></td>
					<td>Addresses in the subnet, default 64.</td>
			</tr>
			<tr>
					<td><code>ipAddresses[]</code></td>
					<td>Specific CIDRs instead of a size.</td>
			</tr>
			<tr>
					<td><code>subnetDHCPConfig.mode</code></td>
					<td><code>DHCPDeactivated</code> (default), <code>DHCPServer</code>, <code>DHCPRelay</code>; <code>dhcpServerAdditionalConfig.reservedIPRanges</code> keeps ranges out of the pool.</td>
			</tr>
			<tr>
					<td><code>advancedConfig</code></td>
					<td><code>staticIPAllocation.enabled</code>, <code>connectivityState</code> (<code>Connected</code> default, <code>Disconnected</code>), <code>gatewayAddresses</code>, <code>dhcpServerAddresses</code>.</td>
			</tr>
			<tr>
					<td><code>vpcName</code></td>
					<td>The VPC, when it isn&rsquo;t the namespace&rsquo;s own.</td>
			</tr>
			<tr>
					<td><code>vlanConnectionName</code></td>
					<td>A subnet backed by a distributed VLAN connection; not in the designer&rsquo;s form.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (15)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.accessMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Private</code>, <code>Public</code>, <code>PrivateTGW</code>, <code>L2Only</code></td>
					<td>Access mode of Subnet, accessible only from within VPC or from outside VPC.</td>
			</tr>
			<tr>
					<td><code>spec.advancedConfig</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{dhcpServerAddresses: [10.200.0.2/28], gatewayAddresses: [10.200.0.1/28]}</code></td>
					<td>VPC Subnet advanced configuration.</td>
			</tr>
			<tr>
					<td><code>spec.advancedConfig.connectivityState</code></td>
					<td>string</td>
					<td></td>
					<td><code>Connected</code>, <code>Disconnected</code>; default <code>Connected</code></td>
					<td>Connectivity status of the Subnet from other Subnets of the VPC. The default value is &ldquo;Connected&rdquo;.</td>
			</tr>
			<tr>
					<td><code>spec.advancedConfig.dhcpServerAddresses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.2/28]</code></td>
					<td>DHCPServerAddresses specifies custom DHCP server IP addresses for the Subnet.</td>
			</tr>
			<tr>
					<td><code>spec.advancedConfig.gatewayAddresses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.1/28]</code></td>
					<td>GatewayAddresses specifies custom gateway IP addresses for the Subnet.</td>
			</tr>
			<tr>
					<td><code>spec.advancedConfig.staticIPAllocation</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{enabled: true}</code></td>
					<td>Static IP allocation for VPC Subnet Ports.</td>
			</tr>
			<tr>
					<td><code>spec.advancedConfig.staticIPAllocation.enabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Activate or deactivate static IP allocation for VPC Subnet Ports. If the DHCP mode is DHCPDeactivated or not set, its default value is true.</td>
			</tr>
			<tr>
					<td><code>spec.ipAddresses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.0/28]</code></td>
					<td>Subnet CIDRS.</td>
			</tr>
			<tr>
					<td><code>spec.ipv4SubnetSize</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>32</code></td>
					<td>Size of Subnet based upon estimated workload count.</td>
			</tr>
			<tr>
					<td><code>spec.subnetDHCPConfig</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{dhcpServerAdditionalConfig: {reservedIPRanges: [10.200.0.10-10.200.0.15]}, ...}</code></td>
					<td>DHCP configuration for Subnet.</td>
			</tr>
			<tr>
					<td><code>spec.subnetDHCPConfig.dhcpServerAdditionalConfig</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{reservedIPRanges: [10.200.0.10-10.200.0.15]}</code></td>
					<td>Additional DHCP server config for a VPC Subnet.</td>
			</tr>
			<tr>
					<td><code>spec.subnetDHCPConfig.dhcpServerAdditionalConfig.reservedIPRanges</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[10.200.0.10-10.200.0.15]</code></td>
					<td>Reserved IP ranges. Supported formats include: [&ldquo;192.168.1.1&rdquo;, &ldquo;192.168.1.3-192.168.1.100&rdquo;]</td>
			</tr>
			<tr>
					<td><code>spec.subnetDHCPConfig.mode</code></td>
					<td>string</td>
					<td></td>
					<td><code>DHCPServer</code>, <code>DHCPRelay</code>, <code>DHCPDeactivated</code></td>
					<td>DHCP Mode. DHCPDeactivated will be used if it is not defined. It cannot switch from DHCPDeactivated to DHCPServer or DHCPRelay.</td>
			</tr>
			<tr>
					<td><code>spec.vlanConnectionName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;distributed VLAN connection&gt;</code></td>
					<td>Distributed VLAN Connection name.</td>
			</tr>
			<tr>
					<td><code>spec.vpcName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>${resource.vpc.name}</code></td>
					<td>VPC name of the Subnet.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Minimal, our lab&rsquo;s management subnet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">snMgmt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">crd.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Subnet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sn-mgmt}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w"> </span>{<span class="nt">accessMode</span><span class="p">:</span><span class="w"> </span><span class="nt">Private, ipv4SubnetSize</span><span class="p">:</span><span class="w"> </span><span class="m">32</span>}<span class="w">
</span></span></span></code></pre></div><p>A VM joins it by name, in an interface:
<code>network: {apiVersion: crd.nsx.vmware.com/v1alpha1, kind: Subnet, name: sn-mgmt}</code>.</p>
<p><strong>Status worth reading:</strong> <code>status.networkAddresses</code>, <code>status.gatewayAddresses</code>,
<code>status.conditions</code> (<code>Realized</code>).</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>DHCP is off by default. VMs still get addresses: VM Operator takes one
from the subnet and hands it to the guest through the bootstrap provider.</li>
<li>Subnets are NSX objects of the VPC. After a deployment is deleted they can
outlive it for a few minutes; wait until the VPC lists none before reusing
the addresses.</li>
</ul>
<h2 id="persistent-volume-claim">Persistent Volume Claim</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: v1</code>,
<code>kind: PersistentVolumeClaim</code>. A disk from a storage class, for a VM&rsquo;s
<code>volumes</code> or a pod.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>storageClassName</code></td>
					<td>A storage class the namespace has.</td>
			</tr>
			<tr>
					<td><code>resources.requests.storage</code></td>
					<td>The size: <code>100Gi</code>.</td>
			</tr>
			<tr>
					<td><code>accessModes[]</code></td>
					<td><code>ReadWriteOnce</code> for a VM disk; <code>ReadWriteMany</code> where the storage supports it.</td>
			</tr>
			<tr>
					<td><code>volumeMode</code></td>
					<td><code>Filesystem</code> or <code>Block</code>.</td>
			</tr>
			<tr>
					<td><code>dataSource</code>, <code>dataSourceRef</code></td>
					<td>Restore from a snapshot or clone another claim.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (23)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.accessModes</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[ReadWriteOnce]</code></td>
					<td>accessModes contains the desired access modes the volume should have.</td>
			</tr>
			<tr>
					<td><code>spec.dataSource</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{apiGroup: snapshot.storage.k8s.io, kind: &lt;kind&gt;}</code></td>
					<td>TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.</td>
			</tr>
			<tr>
					<td><code>spec.dataSource.apiGroup</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>snapshot.storage.k8s.io</code></td>
					<td>APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group.</td>
			</tr>
			<tr>
					<td><code>spec.dataSource.kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;kind&gt;</code></td>
					<td>Kind is the type of resource being referenced</td>
			</tr>
			<tr>
					<td><code>spec.dataSource.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;name&gt;</code></td>
					<td>Name is the name of resource being referenced</td>
			</tr>
			<tr>
					<td><code>spec.dataSourceRef</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{apiGroup: snapshot.storage.k8s.io, namespace: ns-source}</code></td>
					<td>TypedObjectReference contains enough information to let you locate the typed referenced object</td>
			</tr>
			<tr>
					<td><code>spec.dataSourceRef.apiGroup</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>snapshot.storage.k8s.io</code></td>
					<td>APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group.</td>
			</tr>
			<tr>
					<td><code>spec.dataSourceRef.kind</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;kind&gt;</code></td>
					<td>Kind is the type of resource being referenced</td>
			</tr>
			<tr>
					<td><code>spec.dataSourceRef.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;name&gt;</code></td>
					<td>Name is the name of resource being referenced</td>
			</tr>
			<tr>
					<td><code>spec.dataSourceRef.namespace</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ns-source</code></td>
					<td>Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to &hellip;</td>
			</tr>
			<tr>
					<td><code>spec.resources</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{limits: {storage: 20Gi}, requests: {storage: 20Gi}}</code></td>
					<td>VolumeResourceRequirements describes the storage resource requirements for a volume.</td>
			</tr>
			<tr>
					<td><code>spec.resources.limits</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{storage: 20Gi}</code></td>
					<td>Limits describes the maximum amount of compute resources allowed.</td>
			</tr>
			<tr>
					<td><code>spec.resources.requests</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{storage: 20Gi}</code></td>
					<td>Requests describes the minimum amount of compute resources required.</td>
			</tr>
			<tr>
					<td><code>spec.selector</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{matchExpressions: [{key: app, operator: In}], matchLabels: {tier: fast}}</code></td>
					<td>A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.selector.matchExpressions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: app, operator: In}]</code></td>
					<td>matchExpressions is a list of label selector requirements. The requirements are ANDed.</td>
			</tr>
			<tr>
					<td><code>spec.selector.matchExpressions[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>app</code></td>
					<td>key is the label key that the selector applies to.</td>
			</tr>
			<tr>
					<td><code>spec.selector.matchExpressions[].operator</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>In</code></td>
					<td>operator represents a key&rsquo;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</td>
			</tr>
			<tr>
					<td><code>spec.selector.matchExpressions[].values</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[fast]</code></td>
					<td>values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.</td>
			</tr>
			<tr>
					<td><code>spec.selector.matchLabels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{tier: fast}</code></td>
					<td>matchLabels is a map of {key,value} pairs.</td>
			</tr>
			<tr>
					<td><code>spec.storageClassName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vsan-default-storage-policy</code></td>
					<td>storageClassName is the name of the StorageClass required by the claim.</td>
			</tr>
			<tr>
					<td><code>spec.volumeAttributesClassName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;volume attributes class&gt;</code></td>
					<td>volumeAttributesClassName may be used to set the VolumeAttributesClass used by this claim.</td>
			</tr>
			<tr>
					<td><code>spec.volumeMode</code></td>
					<td>string</td>
					<td></td>
					<td><code>Block</code>, <code>Filesystem</code></td>
					<td>volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.</td>
			</tr>
			<tr>
					<td><code>spec.volumeName</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;existing PersistentVolume&gt;</code></td>
					<td>volumeName is the binding reference to the PersistentVolume backing this claim.</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dataDisk</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolumeClaim</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-01-data}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">ReadWriteOnce]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">vsan-default-storage-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">requests</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">5Gi</span><span class="w">
</span></span></span></code></pre></div><p><strong>Status worth reading:</strong> <code>status.phase</code> (<code>Bound</code>), <code>status.capacity.storage</code>.</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>The designer&rsquo;s form calls the field <code>accessMode</code>; the Supervisor refuses
that spelling (<code>unknown field &quot;spec.accessMode&quot;</code>).</li>
<li>A <code>-latebinding</code> storage class (WaitForFirstConsumer) puts no constraint on
where the VM lands; our nested hosts&rsquo; vSAN capacity disks use one.</li>
</ul>
<h2 id="secret">Secret</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: v1</code>, <code>kind: Secret</code>. Key/value
data in the namespace. In a blueprint it mostly carries a VM&rsquo;s cloud-init or
Sysprep data, or a password a VM&rsquo;s <code>cloudConfig</code> references.</p>
<table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>stringData</code></td>
					<td>Plain values; the Supervisor encodes them. The easy one in a blueprint.</td>
			</tr>
			<tr>
					<td><code>data</code></td>
					<td>Base64-encoded values.</td>
			</tr>
			<tr>
					<td><code>type</code></td>
					<td><code>Opaque</code> for your own data; <code>kubernetes.io/tls</code> and the other standard types.</td>
			</tr>
			<tr>
					<td><code>immutable</code></td>
					<td><code>true</code> stops changes after creation.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (4)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>data</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{password: &lt;base64&gt;}</code></td>
					<td>Data contains the secret data. Each key must consist of alphanumeric characters, &lsquo;-&rsquo;, &lsquo;_&rsquo; or &lsquo;.&rsquo;.</td>
			</tr>
			<tr>
					<td><code>immutable</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Immutable, if set to true, ensures that data stored in the Secret cannot be updated (only object metadata can be modified).</td>
			</tr>
			<tr>
					<td><code>stringData</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{password: ${input.password}}</code></td>
					<td>stringData allows specifying non-binary secret data in string form. It is provided as a write-only input field for convenience.</td>
			</tr>
			<tr>
					<td><code>type</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Opaque</code></td>
					<td>Used to facilitate programmatic handling of secret data.</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">jumpConfig</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">jump-bootstrap}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Opaque</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">stringData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">user-data</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">            #cloud-config
</span></span></span><span class="line"><span class="cl"><span class="sd">            users:
</span></span></span><span class="line"><span class="cl"><span class="sd">              - name: student
</span></span></span><span class="line"><span class="cl"><span class="sd">                passwd: ${input.jumpPassword}</span><span class="w">
</span></span></span></code></pre></div><p><strong>Gotcha:</strong> an input marked <code>encrypted: true</code> stays hidden in the request,
but whatever a Secret holds can be read by anyone allowed to read Secrets in
the namespace.</p>
<h2 id="kubernetes-cluster">Kubernetes Cluster</h2>
<p><code>CCI.Supervisor.Resource</code> with <code>apiVersion: cluster.x-k8s.io/v1beta1</code>,
<code>kind: Cluster</code>. A VKS cluster, described by a ClusterClass topology. The
designer&rsquo;s schema stops at <code>topology.variables</code>. What the variables can be
comes from the ClusterClass: <code>builtin-generic-v3.6.0</code> on our platform.</p>
<table>
	<thead>
			<tr>
					<th><code>spec</code> field</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>clusterNetwork</code></td>
					<td><strong>Required.</strong> <code>pods.cidrBlocks</code>, <code>services.cidrBlocks</code>, <code>serviceDomain</code>. Without <code>services</code> VKS refuses the cluster: <code>spec.ClusterNetwork.Services must be defined</code>.</td>
			</tr>
			<tr>
					<td><code>topology.class</code></td>
					<td><strong>Required.</strong> The ClusterClass.</td>
			</tr>
			<tr>
					<td><code>topology.classNamespace</code></td>
					<td>Where the ClusterClass lives; not in the designer&rsquo;s form. See the gotchas.</td>
			</tr>
			<tr>
					<td><code>topology.version</code></td>
					<td><strong>Required.</strong> A Kubernetes release the Supervisor offers, for example <code>v1.35.5+vmware.1-vkr.1</code>.</td>
			</tr>
			<tr>
					<td><code>topology.controlPlane</code></td>
					<td><code>replicas</code> (1 or 3), <code>metadata</code>, <code>machineHealthCheck</code>, node drain and deletion timeouts.</td>
			</tr>
			<tr>
					<td><code>topology.workers.machineDeployments[]</code></td>
					<td><code>class: node-pool</code> (the only worker class), <code>name</code>, <code>replicas</code>, and <code>variables.overrides[]</code> per pool.</td>
			</tr>
			<tr>
					<td><code>topology.variables[]</code></td>
					<td><code>name</code> and <code>value</code> pairs, below.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (85)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>spec.availabilityGates</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{conditionType: &lt;condition type&gt;, polarity: Positive}]</code></td>
					<td>availabilityGates specifies additional conditions to include when evaluating Cluster Available condition.</td>
			</tr>
			<tr>
					<td><code>spec.availabilityGates[].conditionType</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;condition type&gt;</code></td>
					<td>conditionType refers to a condition with matching type in the Cluster&rsquo;s condition list. If the conditions doesn&rsquo;t exist, it will be treated as unknown.</td>
			</tr>
			<tr>
					<td><code>spec.availabilityGates[].polarity</code></td>
					<td>string</td>
					<td></td>
					<td><code>Positive</code>, <code>Negative</code></td>
					<td>polarity of the conditionType specified in this availabilityGate. Valid values are Positive, Negative and omitted. When omitted, the default behaviour will be Positive.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{pods: {cidrBlocks: [192.168.156.0/20]}, serviceDomain: cluster.local}</code></td>
					<td>clusterNetwork represents the cluster network configuration.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork.apiServerPort</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>6443</code></td>
					<td>apiServerPort specifies the port the API Server should bind to. Defaults to 6443.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork.pods</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cidrBlocks: [192.168.156.0/20]}</code></td>
					<td>pods is the network ranges from which Pod networks are allocated.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork.pods.cidrBlocks</code></td>
					<td>array of string</td>
					<td>yes</td>
					<td>e.g. <code>[192.168.156.0/20]</code></td>
					<td>cidrBlocks is a list of CIDR blocks.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork.serviceDomain</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>cluster.local</code></td>
					<td>serviceDomain is the domain name for services.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork.services</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cidrBlocks: [10.96.0.0/12]}</code></td>
					<td>services is the network ranges from which service VIPs are allocated.</td>
			</tr>
			<tr>
					<td><code>spec.clusterNetwork.services.cidrBlocks</code></td>
					<td>array of string</td>
					<td>yes</td>
					<td>e.g. <code>[10.96.0.0/12]</code></td>
					<td>cidrBlocks is a list of CIDR blocks.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneEndpoint</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{host: 192.168.144.40, port: 6443}</code></td>
					<td>controlPlaneEndpoint represents the endpoint used to communicate with the control plane.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneEndpoint.host</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>192.168.144.40</code></td>
					<td>host is the hostname on which the API server is serving.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneEndpoint.port</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>6443</code></td>
					<td>port is the port on which the API server is serving.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{kind: KubeadmControlPlane, name: lab-vks-cp}</code></td>
					<td>controlPlaneRef is an optional reference to a provider-specific resource that holds the details for provisioning the Control Plane for a Cluster.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.apiVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>controlplane.cluster.x-k8s.io/v1beta1</code></td>
					<td>API version of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.fieldPath</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(set by the platform)</code></td>
					<td>If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.kind</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>KubeadmControlPlane</code></td>
					<td>Kind of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.name</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>lab-vks-cp</code></td>
					<td>Name of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.namespace</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ns-lab</code></td>
					<td>Namespace of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.resourceVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(set by the platform)</code></td>
					<td>Specific resourceVersion to which this reference is made, if any.</td>
			</tr>
			<tr>
					<td><code>spec.controlPlaneRef.uid</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(set by the platform)</code></td>
					<td>UID of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{kind: VSphereCluster, name: lab-vks}</code></td>
					<td>infrastructureRef is a reference to a provider-specific resource that holds the details for provisioning infrastructure for a cluster in said provider.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.apiVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vmware.infrastructure.cluster.x-k8s.io/v1beta1</code></td>
					<td>API version of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.fieldPath</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(set by the platform)</code></td>
					<td>If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.kind</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>VSphereCluster</code></td>
					<td>Kind of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.name</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>lab-vks</code></td>
					<td>Name of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.namespace</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ns-lab</code></td>
					<td>Namespace of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.resourceVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(set by the platform)</code></td>
					<td>Specific resourceVersion to which this reference is made, if any.</td>
			</tr>
			<tr>
					<td><code>spec.infrastructureRef.uid</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>(set by the platform)</code></td>
					<td>UID of the referent.</td>
			</tr>
			<tr>
					<td><code>spec.paused</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>paused can be used to prevent controllers from processing the Cluster and all its associated objects.</td>
			</tr>
			<tr>
					<td><code>spec.topology</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{class: builtin-generic-v3.6.0, classNamespace: vmware-system-vks-public}</code></td>
					<td>topology encapsulates the topology for the cluster.</td>
			</tr>
			<tr>
					<td><code>spec.topology.class</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>builtin-generic-v3.6.0</code></td>
					<td>class is the name of the ClusterClass object to create the topology.</td>
			</tr>
			<tr>
					<td><code>spec.topology.classNamespace</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vmware-system-vks-public</code></td>
					<td>classNamespace is the namespace of the ClusterClass that should be used for the topology. If classNamespace is empty or not set, it is defaulted to the namespace of the Cluster object.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{replicas: 1, machineHealthCheck: {maxUnhealthy: 40%, nodeStartupTimeout: 10m}}</code></td>
					<td>controlPlane describes the cluster control plane.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{maxUnhealthy: 40%, nodeStartupTimeout: 10m}</code></td>
					<td>machineHealthCheck allows to enable, disable and override the MachineHealthCheck configuration in the ClusterClass for this control plane.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck.enable</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>enable controls if a MachineHealthCheck should be created for the target machines. If false: No MachineHealthCheck will be created.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck.maxUnhealthy</code></td>
					<td>int or string</td>
					<td></td>
					<td>e.g. <code>40%</code></td>
					<td>maxUnhealthy specifies the maximum number of unhealthy machines allowed. Any further remediation is only allowed if at most &ldquo;maxUnhealthy&rdquo; machines selected by &ldquo;selector&rdquo; are not healthy.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck.nodeStartupTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeStartupTimeout allows to set the maximum time for MachineHealthCheck to consider a Machine unhealthy if a corresponding Node isn&rsquo;t associated through a <code>Spec.ProviderID</code> field.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck.remediationTemplate</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{apiVersion: &lt;group&gt;/&lt;version&gt;, kind: &lt;remediation template kind&gt;, name: &lt;name&gt;}</code></td>
					<td>remediationTemplate is a reference to a remediation template provided by an infrastructure provider.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck.unhealthyConditions</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{type: Ready, status: Unknown, timeout: 300s}]</code></td>
					<td>unhealthyConditions contains a list of the conditions that determine whether a node is considered unhealthy. The conditions are combined in a logical OR, i.e.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.machineHealthCheck.unhealthyRange</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>'[1-3]'</code></td>
					<td>unhealthyRange specifies the range of unhealthy machines allowed.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.metadata</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{annotations: {owner: team-a}, labels: {app: web}}</code></td>
					<td>metadata is the metadata applied to the ControlPlane and the Machines of the ControlPlane if the ControlPlaneTemplate referenced by the ClusterClass is machine based.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.metadata.annotations</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{owner: team-a}</code></td>
					<td>annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.metadata.labels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{app: web}</code></td>
					<td>labels is a map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.nodeDeletionTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine hosts after the Machine is marked for deletion.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.nodeDrainTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeDrainTimeout is the total amount of time that the controller will spend on draining a node. The default value is 0, meaning that the node can be drained without any time limitations.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.nodeVolumeDetachTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes to be detached.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.readinessGates</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{conditionType: &lt;condition type&gt;, polarity: Positive}]</code></td>
					<td>readinessGates specifies additional conditions to include when evaluating Machine Ready condition. This field can be used e.g.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.readinessGates[].conditionType</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;condition type&gt;</code></td>
					<td>conditionType refers to a condition with matching type in the Machine&rsquo;s condition list. If the conditions doesn&rsquo;t exist, it will be treated as unknown.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.readinessGates[].polarity</code></td>
					<td>string</td>
					<td></td>
					<td><code>Positive</code>, <code>Negative</code></td>
					<td>polarity of the conditionType specified in this readinessGate. Valid values are Positive, Negative and omitted. When omitted, the default behaviour will be Positive.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.replicas</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1</code></td>
					<td>replicas is the number of control plane nodes.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.variables</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{overrides: [{name: vmClass, value: best-effort-medium}]}</code></td>
					<td>variables can be used to customize the ControlPlane through patches.</td>
			</tr>
			<tr>
					<td><code>spec.topology.controlPlane.variables.overrides</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: vmClass, value: best-effort-medium}]</code></td>
					<td>overrides can be used to override Cluster level variables.</td>
			</tr>
			<tr>
					<td><code>spec.topology.rolloutAfter</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>2026-10-01T22:00:00Z</code></td>
					<td>rolloutAfter performs a rollout of the entire cluster one component at a time, control plane first and then machine deployments.</td>
			</tr>
			<tr>
					<td><code>spec.topology.variables</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: vmClass, value: best-effort-small}]</code></td>
					<td>variables can be used to customize the Cluster through patches. They must comply to the corresponding VariableClasses defined in the ClusterClass.</td>
			</tr>
			<tr>
					<td><code>spec.topology.variables[].definitionFrom</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;patch name&gt;</code></td>
					<td>definitionFrom specifies where the definition of this Variable is from. Deprecated: This field is deprecated, must not be set anymore and is going to be removed in the next apiVersion.</td>
			</tr>
			<tr>
					<td><code>spec.topology.variables[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vmClass</code></td>
					<td>name of the variable.</td>
			</tr>
			<tr>
					<td><code>spec.topology.variables[].value</code></td>
					<td>any</td>
					<td>yes</td>
					<td>e.g. <code>best-effort-small</code></td>
					<td>value of the variable. Note: the value will be validated against the schema of the corresponding ClusterClassVariable from the ClusterClass.</td>
			</tr>
			<tr>
					<td><code>spec.topology.version</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>v1.35.5+vmware.1-vkr.1</code></td>
					<td>version is the Kubernetes version of the cluster.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{machineDeployments: [{name: np-1, class: node-pool}], machinePools: [{name: mp-1, ...}]}</code></td>
					<td>workers encapsulates the different constructs that form the worker nodes for the cluster.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: np-1, class: node-pool}]</code></td>
					<td>machineDeployments is a list of machine deployments in the cluster.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].class</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>node-pool</code></td>
					<td>class is the name of the MachineDeploymentClass used to create the set of worker nodes.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].failureDomain</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>domain-c9</code></td>
					<td>failureDomain is the failure domain the machines will be created in. Must match a key in the FailureDomains map stored on the cluster object.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].machineHealthCheck</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{enable: true}</code></td>
					<td>machineHealthCheck allows to enable, disable and override the MachineHealthCheck configuration in the ClusterClass for this MachineDeployment.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].metadata</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{labels: {pool: np-1}}</code></td>
					<td>metadata is the metadata applied to the MachineDeployment and the machines of the MachineDeployment. At runtime this metadata is merged with the corresponding metadata from the ClusterClass.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].minReadySeconds</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>10</code></td>
					<td>minReadySeconds is the minimum number of seconds for which a newly created machine should be ready. Defaults to 0 (machine will be considered available as soon as it is ready)</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>np-1</code></td>
					<td>name is the unique identifier for this MachineDeploymentTopology. The value is used with other unique identifiers to create a MachineDeployment&rsquo;s Name (e.g.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].nodeDeletionTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine hosts after the Machine is marked for deletion.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].nodeDrainTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeDrainTimeout is the total amount of time that the controller will spend on draining a node. The default value is 0, meaning that the node can be drained without any time limitations.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].nodeVolumeDetachTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes to be detached.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].readinessGates</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{conditionType: &lt;condition type&gt;}]</code></td>
					<td>readinessGates specifies additional conditions to include when evaluating Machine Ready condition. This field can be used e.g.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].replicas</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1</code></td>
					<td>replicas is the number of worker nodes belonging to this set.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].strategy</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{type: RollingUpdate, rollingUpdate: {maxSurge: 1}}</code></td>
					<td>strategy is the deployment strategy to use to replace existing machines with new ones.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machineDeployments[].variables</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{overrides: [{name: vmClass, value: best-effort-large}]}</code></td>
					<td>variables can be used to customize the MachineDeployment through patches.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: mp-1, class: &lt;machine pool class&gt;}]</code></td>
					<td>machinePools is a list of machine pools in the cluster.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].class</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;machine pool class&gt;</code></td>
					<td>class is the name of the MachinePoolClass used to create the pool of worker nodes.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].failureDomains</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[domain-c9]</code></td>
					<td>failureDomains is the list of failure domains the machine pool will be created in. Must match a key in the FailureDomains map stored on the cluster object.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].metadata</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{labels: {pool: mp-1}}</code></td>
					<td>metadata is the metadata applied to the MachinePool. At runtime this metadata is merged with the corresponding metadata from the ClusterClass.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].minReadySeconds</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>10</code></td>
					<td>minReadySeconds is the minimum number of seconds for which a newly created machine pool should be ready. Defaults to 0 (machine will be considered available as soon as it is ready)</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>mp-1</code></td>
					<td>name is the unique identifier for this MachinePoolTopology. The value is used with other unique identifiers to create a MachinePool&rsquo;s Name (e.g.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].nodeDeletionTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeDeletionTimeout defines how long the controller will attempt to delete the Node that the MachinePool hosts after the MachinePool is marked for deletion.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].nodeDrainTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeDrainTimeout is the total amount of time that the controller will spend on draining a node. The default value is 0, meaning that the node can be drained without any time limitations.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].nodeVolumeDetachTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10m</code></td>
					<td>nodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes to be detached.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].replicas</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>2</code></td>
					<td>replicas is the number of nodes belonging to this pool.</td>
			</tr>
			<tr>
					<td><code>spec.topology.workers.machinePools[].variables</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{overrides: [{name: vmClass, value: best-effort-large}]}</code></td>
					<td>variables can be used to customize the MachinePool through patches.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>The ClusterClass&rsquo;s variables (<code>builtin-generic-v3.6.0</code>); <code>vmClass</code> and
<code>storageClass</code> are required:</p>
<table>
	<thead>
			<tr>
					<th>Variable</th>
					<th>What it sets</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>vmClass</code></td>
					<td>The VM class for the nodes.</td>
			</tr>
			<tr>
					<td><code>storageClass</code></td>
					<td>The storage class for node disks.</td>
			</tr>
			<tr>
					<td><code>volumes</code></td>
					<td>Extra node disks: <code>name</code>, <code>capacity</code>, <code>mountPath</code>, <code>storageClass</code>.</td>
			</tr>
			<tr>
					<td><code>node</code></td>
					<td><code>labels</code>, <code>taints</code> and <code>firewall</code> for the nodes.</td>
			</tr>
			<tr>
					<td><code>osConfiguration</code></td>
					<td><code>ntp.servers</code>, <code>trust.additionalTrustedCAs</code>, <code>systemProxy</code> (<code>http</code>, <code>https</code>, <code>noProxy</code>), <code>user</code> (an administrator and its SSH key), <code>sshd</code>, <code>fips</code>, <code>grub</code>, <code>directoryJoin</code>, <code>ubuntuPro</code>, <code>tuned</code>, <code>securityContext</code>.</td>
			</tr>
			<tr>
					<td><code>kubernetes</code></td>
					<td><code>endpointFQDNs</code>, <code>certificateRotation</code> (on by default), and API server, kubelet, controller-manager and etcd settings.</td>
			</tr>
			<tr>
					<td><code>networks</code></td>
					<td>The nodes&rsquo; interfaces: one primary and optional secondary networks.</td>
			</tr>
			<tr>
					<td><code>resourceConfiguration</code></td>
					<td><code>systemReserved</code> CPU and memory for the kubelet.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions</code></td>
					<td><code>persistentVolumes</code>: which storage classes the cluster&rsquo;s PVCs may use.</td>
			</tr>
			<tr>
					<td><code>bootstrapAddons</code></td>
					<td>The CNI, through <code>cniRef</code>.</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (147)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>bootstrapAddons</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cniRef: {name: &lt;CNI package config&gt;, namespace: &lt;its namespace&gt;}}</code></td>
					<td>BootstrapAddons defines Addons to be installed on Cluster during bootstrapping. Only supported with Kubernetes 1.35 and above.</td>
			</tr>
			<tr>
					<td><code>bootstrapAddons.cniRef</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{name: &lt;CNI package config&gt;, namespace: &lt;its namespace&gt;}</code></td>
					<td>CNI Addon to instantiate for Cluster. Used to select CNI rather than ClusterBootstrap spec.CNI field. Compatible Addon/AddonRelease must exist.</td>
			</tr>
			<tr>
					<td><code>bootstrapAddons.cniRef.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;CNI package config&gt;</code></td>
					<td>Name of the Addon being referenced.</td>
			</tr>
			<tr>
					<td><code>bootstrapAddons.cniRef.namespace</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>&lt;its namespace&gt;</code></td>
					<td>Namespace of the addon being referenced. If not specified, will use the default public namespace defined by the addon manager.</td>
			</tr>
			<tr>
					<td><code>kubeAPIServerFQDNs</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[api.lab.example.com]</code></td>
					<td>Deprecated: This variable is deprecated. Use kubernetes.endpointFQDNs instead. This variable will be removed in a future release.</td>
			</tr>
			<tr>
					<td><code>kubernetes</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{apiServerConfiguration: {logs: {flushFrequency: 5s, verbosity: 2}, ...}}</code></td>
					<td>Kubernetes configures cluster-wide settings for the Kubernetes cluster, typically applied to the control plane. Supported scopes: cluster, controlPlane, workers</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{logs: {flushFrequency: 5s, verbosity: 2}, maxMutatingRequestsInFlight: 200}</code></td>
					<td>APIServerConfiguration contains configuration options for the Kubernetes API server.</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.logs</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{flushFrequency: 5s, verbosity: 2}</code></td>
					<td>Logging configures the logging options for the API server, including log levels, formats, and output destinations. Refer to the Kubernetes component-base logs options for more information.</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.logs.flushFrequency</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>5s</code></td>
					<td>FlushFrequency is the maximum time between log flushes. If specified as a string, it&rsquo;s parsed as a duration (e.g., &ldquo;1s&rdquo;).</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.logs.format</code></td>
					<td>string</td>
					<td></td>
					<td><code>text</code>, <code>json</code></td>
					<td>Format specifies the structure of log messages. Supported values are &ldquo;text&rdquo; (default) and &ldquo;json&rdquo;. Corresponds to &ndash;logging-format flag.</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.logs.verbosity</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>2</code></td>
					<td>Verbosity is the threshold that determines which log messages are logged. Default is zero which logs only the most important messages.</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.maxMutatingRequestsInFlight</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>200</code></td>
					<td>MaxMutatingRequestsInFlight is the maximum number of parallel mutating requests. Every further request has to wait.</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.maxRequestsInFlight</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>400</code></td>
					<td>MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further request has to wait.</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.profiling</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>Profiling enables profiling via web interface host:port/debug/pprof/ Default: false</td>
			</tr>
			<tr>
					<td><code>kubernetes.apiServerConfiguration.requestTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>60s</code></td>
					<td>RequestTimeout is the duration after which all non-long-running requests will be timed out. Corresponds to the &ndash;request-timeout flag.</td>
			</tr>
			<tr>
					<td><code>kubernetes.certificateRotation</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{enabled: true, renewalDaysBeforeExpiry: 90}</code></td>
					<td>CertificateRotation configures options for the automatic rotation of control plane certificates which have a default validity of 12 months.</td>
			</tr>
			<tr>
					<td><code>kubernetes.certificateRotation.enabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>true</code></td>
					<td>Enabled controls enablement of auto certificate rotation</td>
			</tr>
			<tr>
					<td><code>kubernetes.certificateRotation.renewalDaysBeforeExpiry</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>90</code></td>
					<td>RenewalDaysBeforeExpiry states the number of days before certificate expiry to initiate the renewal of certificates.</td>
			</tr>
			<tr>
					<td><code>kubernetes.endpointFQDNs</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[api.lab.example.com]</code></td>
					<td>EndpointFQDNs Configure FQDN aliases for the control plane endpoint for example to allow users to connect to the cluster using <a href="https://k8s.prod.example.com/">https://k8s.prod.example.com/</a></td>
			</tr>
			<tr>
					<td><code>kubernetes.etcdConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{maximumDBSizeGiB: 8}</code></td>
					<td>EtcdConfiguration contains configuration options for the etcd database used by Kubernetes. These settings control etcd behavior including database size limits and performance tuning.</td>
			</tr>
			<tr>
					<td><code>kubernetes.etcdConfiguration.maximumDBSizeGiB</code></td>
					<td>integer</td>
					<td>yes</td>
					<td>e.g. <code>8</code></td>
					<td>MaximumDBSizeGiB specifies the maximum size of the etcd database in GiB. This value is used to set &ndash;quota-backend-bytes for etcd.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeControllerManagerConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{terminatedPodGCThreshold: 1000}</code></td>
					<td>KubeControllerManagerConfiguration contains configuration options for the kube-controller-manager. Supported scopes: cluster, controlPlane</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeControllerManagerConfiguration.terminatedPodGCThreshold</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1000</code></td>
					<td>TerminatedPodGCThreshold is the number of terminated pods that can exist before the terminated pod garbage collector starts deleting terminated pods.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{allowedUnsafeSysctls: [net.core.somaxconn], eventBurst: 100}</code></td>
					<td>KubeletConfiguration contains configuration options for the kubelet running on worker nodes.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.allowedUnsafeSysctls</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[net.core.somaxconn]</code></td>
					<td>AllowedUnsafeSysctls is a comma separated allowlist of unsafe sysctls or sysctl patterns (ending in <code>*</code>). All safe sysctls are enabled by default.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.containerLogMaxFiles</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>5</code></td>
					<td>ContainerLogMaxFiles is the maximum number of container log files that can be present for a container. Default: 5</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.containerLogMaxSizeMiB</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>10</code></td>
					<td>ContainerLogMaxSize defines the maximum size of the container log file before it is rotated in MiB.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.eventBurst</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>100</code></td>
					<td>EventBurst is the maximum size of a burst of event creations, temporarily allows event creations to burst to this number, while still not exceeding eventRecordQPS.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.eventRecordQPS</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>50</code></td>
					<td>EventRecordQPS is the maximum event creations per second. If 0, there is no limit enforced. Corresponds to &ndash;event-qps kubelet flag.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.healthzBindAddress</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>127.0.0.1</code></td>
					<td>HealthzBindAddress is the IP address for the healthz server to serve on. Default: &ldquo;127.0.0.1&rdquo;</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.imageGCHighThresholdPercent</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>85</code></td>
					<td>ImageGCHighThresholdPercent is the percent of disk usage after which image garbage collection is always run. The percent is calculated as this field value out of 100.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.imageGCLowThresholdPercent</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>80</code></td>
					<td>ImageGCLowThresholdPercent is the percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.imageMaximumGCAge</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>168h</code></td>
					<td>ImageMaximumGCAge is the maximum age an image can be unused before it is garbage collected.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.imageMinimumGCAge</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>2m</code></td>
					<td>ImageMinimumGCAge is the minimum age for an unused image before it is garbage collected. Default: &ldquo;2m&rdquo;</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.imagePullCredentialsVerificationPolicy</code></td>
					<td>string</td>
					<td></td>
					<td><code>NeverVerify</code>, <code>NeverVerifyPreloadedImages</code>, <code>NeverVerifyAllowlistedImages</code>, <code>AlwaysVerify</code></td>
					<td>ImagePullCredentialsVerificationPolicy determines how credentials should be verified when pod requests an image that is already present on the node.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.logging</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{flushFrequency: 5s, verbosity: 2}</code></td>
					<td>Logging specifies the logging configuration options for the kubelet. This controls log levels, formats, and output destinations for kubelet logs.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.logging.flushFrequency</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>5s</code></td>
					<td>FlushFrequency is the maximum time between log flushes. If specified as a string, it&rsquo;s parsed as a duration (e.g., &ldquo;1s&rdquo;).</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.logging.format</code></td>
					<td>string</td>
					<td></td>
					<td><code>text</code>, <code>json</code></td>
					<td>Format specifies the structure of log messages. Supported values are &ldquo;text&rdquo; (default) and &ldquo;json&rdquo;. Corresponds to &ndash;logging-format flag.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.logging.verbosity</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>2</code></td>
					<td>Verbosity is the threshold that determines which log messages are logged. Default is zero which logs only the most important messages.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.maxParallelImagePulls</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>5</code></td>
					<td>MaxParallelImagePulls sets the maximum number of image pulls in parallel. This field is only used when SerializeImagePulls is false.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.maxPods</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>110</code></td>
					<td>MaxPods is the number of pods that can run on this Kubelet. Default: 110 NOTE: By default, the maximum allowed value is 250.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.podPidsLimit</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>4096</code></td>
					<td>PodPidsLimit is the maximum number of PIDs in any pod. Use Kubelet default (-1) when omitted. Default: nil</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.preloadedImagesVerificationAllowlist</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[registry.example.local/*]</code></td>
					<td>PreloadedImagesVerificationAllowlist specifies a list of images that are exempted from credential reverification for the &ldquo;NeverVerifyAllowlistedImages&rdquo; <code>imagePullCredentialsVerificationPolicy</code>.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.registryBurst</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>10</code></td>
					<td>RegistryBurst is the maximum size of bursty pulls, temporarily allows pulls to burst to this number, while still not exceeding registryPullQPS.</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.registryPullQPS</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>5</code></td>
					<td>RegistryPullQPS is the limit of registry pulls per second. Set to 0 for no limit. Default: 5</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.serializeImagePulls</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>SerializeImagePulls when enabled, tells the Kubelet to pull images one at a time. Default: true</td>
			</tr>
			<tr>
					<td><code>kubernetes.kubeletConfiguration.streamingConnectionIdleTimeout</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>4h</code></td>
					<td>StreamingConnectionIdleTimeout is the maximum time a streaming connection can be idle before the connection is automatically closed.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{podSecurityStandard: {auditVersion: latest, enforceVersion: latest}, ...}</code></td>
					<td>Security configures Kubernetes specific security settings.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{auditVersion: latest, enforceVersion: latest}</code></td>
					<td>PodSecurityStandard configures the PodSecurityStandard settings for the cluster.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.audit</code></td>
					<td>string</td>
					<td></td>
					<td>``, <code>privileged</code>, <code>baseline</code>, <code>restricted</code></td>
					<td>Audit sets the level for the audit PodSecurityConfiguration mode. Policy violations trigger an audit annotation, but are otherwise allowed One of &ldquo;&rdquo;, privileged, baseline, restricted.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.auditVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>latest</code></td>
					<td>AuditVersion can be used to pin the policy to the version that shipped with a given Kubernetes minor version (e.g. v1.31) when in audit mode.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.deactivated</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Deactivated disables the patches for Pod Security Standard via AdmissionConfiguration.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.enforce</code></td>
					<td>string</td>
					<td></td>
					<td>``, <code>privileged</code>, <code>baseline</code>, <code>restricted</code></td>
					<td>Enforce sets the level for the enforce PodSecurityConfiguration mode. Policy violations cause the pod to be rejected.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.enforceVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>latest</code></td>
					<td>EnforceVersion can be used to pin the policy to the version that shipped with a given Kubernetes minor version (e.g.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.exemptions</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{namespaces: [monitoring]}</code></td>
					<td>Exemptions can be statically configured based on (requesting) user, RuntimeClass, or namespace. A request meeting exemption criteria is ignored by the admission plugin.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.warn</code></td>
					<td>string</td>
					<td></td>
					<td>``, <code>privileged</code>, <code>baseline</code>, <code>restricted</code></td>
					<td>Warn sets the level for the warn PodSecurityConfiguration mode. Policy violations trigger a user-facing warning, but are otherwise allowed.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.podSecurityStandard.warnVersion</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>latest</code></td>
					<td>WarnVersion can be used to pin the policy to the version that shipped with a given Kubernetes minor version (e.g. v1.31) when in warn mode.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.resourceQuotaConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{enabled: false}</code></td>
					<td>ResourceQuotaConfiguration configures the ResourceQuota admission control settings for the cluster.</td>
			</tr>
			<tr>
					<td><code>kubernetes.security.resourceQuotaConfiguration.enabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Enabled enables the patches for ResourceQuotaConfiguration via AdmissionConfiguration.</td>
			</tr>
			<tr>
					<td><code>networks</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{interfaces: {primary: {network: {apiVersion: crd.nsx.vmware.com/v1alpha1, ...}}}}</code></td>
					<td>Networks defines the network configuration for the cluster</td>
			</tr>
			<tr>
					<td><code>networks.interfaces</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{primary: {network: {apiVersion: crd.nsx.vmware.com/v1alpha1, kind: SubnetSet, ...}}}</code></td>
					<td>Interfaces describes one primary (eth0) and zero or more secondary interfaces attached to Node virtual machine.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.primary</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{network: {apiVersion: crd.nsx.vmware.com/v1alpha1, kind: SubnetSet, ...}}</code></td>
					<td>Primary is the primary network interface which is used to connect the Kubernetes primary network for Load balancer, Service discovery, Pod traffic and management traffic etc.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.primary.mtu</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1500</code></td>
					<td>MTU is the Maximum Transmission Unit size in bytes.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.primary.network</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{apiVersion: crd.nsx.vmware.com/v1alpha1, kind: SubnetSet, name: &lt;subnet set&gt;}</code></td>
					<td>Network is the name of the network resource to which this interface is connected.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.primary.routes</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{to: 172.16.0.0/16, via: 10.244.0.1}]</code></td>
					<td>Routes is a list of optional, static routes.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.secondary</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: eth1, network: {apiVersion: crd.nsx.vmware.com/v1alpha1, kind: Subnet, ...}}]</code></td>
					<td>Secondary network is supported with network provider NSX-VPC and vsphere-network.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.secondary[].mtu</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>1500</code></td>
					<td>MTU is the Maximum Transmission Unit size in bytes.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.secondary[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>eth1</code></td>
					<td>Name describes the unique name of this network interface, used to distinguish it from other network interfaces attached to node Virtual Machine.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.secondary[].network</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{apiVersion: crd.nsx.vmware.com/v1alpha1, kind: Subnet, name: storage-net}</code></td>
					<td>Network is the name of the network resource to which this interface is connected.</td>
			</tr>
			<tr>
					<td><code>networks.interfaces.secondary[].routes</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{to: 10.50.0.0/16, via: 10.250.0.1}]</code></td>
					<td>Routes is a list of optional, static routes.</td>
			</tr>
			<tr>
					<td><code>node</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{firewall: {inboundRules: [{fromPort: 30000, protocol: TCP}]}, labels: {workload: web}}</code></td>
					<td>Node configures Kubernetes node specific settings. Supported scopes: cluster, controlPlane, workers</td>
			</tr>
			<tr>
					<td><code>node.firewall</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{inboundRules: [{fromPort: 30000, protocol: TCP}]}</code></td>
					<td>Firewall specifies the firewall configuration that should be created on the node to allow specific kinds of traffic.</td>
			</tr>
			<tr>
					<td><code>node.firewall.inboundRules</code></td>
					<td>array of object</td>
					<td>yes</td>
					<td>e.g. <code>[{fromPort: 30000, protocol: TCP}]</code></td>
					<td>InboundRules is a list of firewall rules that will be configured on each node to allow or deny specific kinds of traffic.</td>
			</tr>
			<tr>
					<td><code>node.firewall.inboundRules[].fromPort</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>30000</code></td>
					<td>FromPort is the low end (inclusive) of the port range that this rule applies to.</td>
			</tr>
			<tr>
					<td><code>node.firewall.inboundRules[].protocol</code></td>
					<td>int or string</td>
					<td>yes</td>
					<td>e.g. <code>TCP</code></td>
					<td>Protocol is the type of traffic that this rule applies to. Allowed protocols include &ldquo;tcp&rdquo;, &ldquo;udp&rdquo;, &ldquo;icmp&rdquo;, or an any valid IANA protocol number.</td>
			</tr>
			<tr>
					<td><code>node.firewall.inboundRules[].source</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>10.0.0.0/8</code></td>
					<td>Source is the CIDR range of the originating traffic that this rule applies to. If unset, the rule will apply to any source network.</td>
			</tr>
			<tr>
					<td><code>node.firewall.inboundRules[].toPort</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>32767</code></td>
					<td>ToPort is the high end (inclusive) of the port range that this rule applies to.</td>
			</tr>
			<tr>
					<td><code>node.labels</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{workload: web}</code></td>
					<td>Labels is a list of user defined name-value pairs</td>
			</tr>
			<tr>
					<td><code>node.taints</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: dedicated, value: gpu}]</code></td>
					<td>Taints specifies the taints the Node API object should be registered with. If this field is unset, i.e. nil, it will be defaulted with a control-plane taint for control-plane nodes.</td>
			</tr>
			<tr>
					<td><code>node.taints[].effect</code></td>
					<td>string</td>
					<td>yes</td>
					<td><code>NoSchedule</code>, <code>PreferNoSchedule</code>, <code>NoExecute</code></td>
					<td>Effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute.</td>
			</tr>
			<tr>
					<td><code>node.taints[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>dedicated</code></td>
					<td>Key is the taint key to be applied to a node.</td>
			</tr>
			<tr>
					<td><code>node.taints[].value</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>gpu</code></td>
					<td>Value is the taint value corresponding to the taint key.</td>
			</tr>
			<tr>
					<td><code>osConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{ntp: {servers: [172.30.0.34]}, ...}</code></td>
					<td>OSConfiguration configures the system settings of nodes that are independent of Kubernetes. Supported scopes: cluster, controlPlane, workers</td>
			</tr>
			<tr>
					<td><code>osConfiguration.directoryJoin</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{credentialSecretRef: &lt;Secret with the join account&gt;, domain: example.local}</code></td>
					<td>DirectoryJoin configures the node to join a Windows Active Directory. Only supported on Windows at present.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.directoryJoin.credentialSecretRef</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret with the join account&gt;</code></td>
					<td>CredentialSecretRef is the name of the secret containing Active Directory join credentials.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.directoryJoin.domain</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>example.local</code></td>
					<td>Domain is the FQDN of the Active Directory Kerberos domain to join.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.directoryJoin.gmsaControlSecurityGroupDN</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>CN=gmsa-k8s,OU=Groups,DC=example,DC=local</code></td>
					<td>GMSAControlSecurityGroupDN is an optional Windows Active Directory security group that has permissions to access the password of the Group Managed Service Accounts.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.directoryJoin.organizationalUnitDN</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>OU=K8s,DC=example,DC=local</code></td>
					<td>OrganizationalUnitDN is an optional organizational unit where the node will be added to in Active Directory. The value will be validated according to <a href="https://tools.ietf.org/html/rfc4514">https://tools.ietf.org/html/rfc4514</a></td>
			</tr>
			<tr>
					<td><code>osConfiguration.fips</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{enabled: false}</code></td>
					<td>FIPS configures FIPS related settings for the Kubernetes cluster to run in FIPS mode. Supported scopes: cluster</td>
			</tr>
			<tr>
					<td><code>osConfiguration.fips.enabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Enable specifies whether FIPS settings are enabled and enforced on the node</td>
			</tr>
			<tr>
					<td><code>osConfiguration.grub</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{password: {secretRef: {name: &lt;Secret&gt;, key: password}, user: root}}</code></td>
					<td>GRUB configures GRUB Boot Loader.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.grub.password</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{secretRef: {name: &lt;Secret&gt;, key: password}, user: root}</code></td>
					<td>Password configures the password protection for GRUB Boot Loader (Only applicable on Linux).</td>
			</tr>
			<tr>
					<td><code>osConfiguration.grub.password.enabled</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>false</code></td>
					<td>Enabled defines if the GRUB Boot Loader must be protected with a password</td>
			</tr>
			<tr>
					<td><code>osConfiguration.grub.password.secretRef</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{name: &lt;Secret&gt;, key: password}</code></td>
					<td>SecretRef is the name of the secret containing the password to protect GRUB Key is the data.key field within the secret containing the password value.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.grub.password.user</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>root</code></td>
					<td>User specifies the username to use for GRUB password protection.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.ntp</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{servers: [172.30.0.34]}</code></td>
					<td>NTP sets the time servers that will be used by nodes in the cluster. By default, NTP servers are inherited from vCenter.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.ntp.servers</code></td>
					<td>array of string</td>
					<td>yes</td>
					<td>e.g. <code>[172.30.0.34]</code></td>
					<td>NTP sets the time servers that will be used by nodes in this cluster. By default, NTP servers are inherited from vCenter.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.securityContext</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{appArmor: {profiles: [{name: &lt;AppArmor profile&gt;}]}}</code></td>
					<td>SecurityContext holds security configurations that will be applied to node.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.securityContext.appArmor</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{profiles: [{name: &lt;AppArmor profile&gt;}]}</code></td>
					<td>AppArmor configures the appArmor profiles of the node. Supported scopes: cluster, controlPlane, workers Only supported on Ubuntu and Photon nodes.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.securityContext.appArmor.profiles</code></td>
					<td>array of object</td>
					<td>yes</td>
					<td>e.g. <code>[{name: &lt;AppArmor profile&gt;}]</code></td>
					<td>Profiles is a list of appArmor profiles to be added to the node.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.sshd</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{banner: Authorised use only}</code></td>
					<td>SSHD configures the sshd config of the node.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.sshd.banner</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>Authorised use only</code></td>
					<td>Banner specifies the login message used for sending a legal warning message before authentication</td>
			</tr>
			<tr>
					<td><code>osConfiguration.systemProxy</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{http: http://proxy.example.local:3128, https: http://proxy.example.local:3128}</code></td>
					<td>SystemProxy configures parameters that reference a proxy server for outbound cluster connections.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.systemProxy.http</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>http://proxy.example.local:3128</code></td>
					<td>HTTP is the proxy server to be used for all http connections. This should be a hostname or dotted numerical IP address.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.systemProxy.https</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>http://proxy.example.local:3128</code></td>
					<td>HTTPS configures the proxy server to be used for all https connections. This should be a hostname or dotted numerical IP address.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.systemProxy.noProxy</code></td>
					<td>array of string</td>
					<td>yes</td>
					<td>e.g. <code>[.example.local, 10.0.0.0/8]</code></td>
					<td>NoProxy configures the list of hostnames and CIDR ranges that should be reached without the configured proxy servers.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.trust</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{additionalTrustedCAs: [{caCert: {secretRef: {name: corp-ca}}}]}</code></td>
					<td>Trust configures system-wide certificate trust for nodes</td>
			</tr>
			<tr>
					<td><code>osConfiguration.trust.additionalTrustedCAs</code></td>
					<td>array of object</td>
					<td>yes</td>
					<td>e.g. <code>[{caCert: {secretRef: {name: corp-ca}}}]</code></td>
					<td>AdditionalTrustedCAs is a list of additional CAs to be added to the system trust store of nodes.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.trust.additionalTrustedCAs[].caCert</code></td>
					<td>object</td>
					<td>yes</td>
					<td>e.g. <code>{secretRef: {name: corp-ca, key: ca.crt}}</code></td>
					<td>SecretContent configures a reference to or content of secret data.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.tuned</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{active: [&lt;tuned profile&gt;], profiles: {&lt;profile name&gt;: &lt;TunedProfile reference&gt;}}</code></td>
					<td>TuneD injects TuneD profiles and activate specified profile on Linux nodes. Only supported on Linux.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.tuned.active</code></td>
					<td>array of string</td>
					<td>yes</td>
					<td>e.g. <code>[&lt;tuned profile&gt;]</code></td>
					<td>Active is a list of tuned profile name will be activated on node.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.tuned.profiles</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{&lt;profile name&gt;: &lt;TunedProfile reference&gt;}</code></td>
					<td>Profiles is a map of tuned profiles will be injected on node. Key is the desired tuned profile name, value is the TunedProfile CR reference which contains the profile content.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.ubuntuPro</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{services: [usg], settings: [{key: &lt;setting&gt;, value: &lt;value&gt;}]}</code></td>
					<td>UbuntuPro configures the Ubuntu Pro subscription of the node. Only supported on Ubuntu.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.ubuntuPro.services</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[usg]</code></td>
					<td>Services specifies the Ubuntu Pro services to be enabled.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.ubuntuPro.settings</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{key: &lt;setting&gt;, value: &lt;value&gt;}]</code></td>
					<td>Settings specifies the Ubuntu Pro client (ubuntu-advantage-tools) settings to be configured.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.ubuntuPro.settings[].key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;setting&gt;</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>osConfiguration.ubuntuPro.settings[].value</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;value&gt;</code></td>
					<td></td>
			</tr>
			<tr>
					<td><code>osConfiguration.ubuntuPro.tokenSecretRef</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret with the Pro token&gt;</code></td>
					<td>TokenSecretRef is the name of the secret containing a valid Ubuntu Pro Subscription token. The secret must have a key token with the content of a valid token.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{passwordSecret: {key: password, name: &lt;Secret&gt;}, ...}</code></td>
					<td>User is an administrative user that will be created on all nodes. If not set, this is defaulted to &ldquo;vmware-system-user&rdquo;.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.password</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{renewalDaysBeforeExpiry: 30}</code></td>
					<td>Password configures the password policy such as password max age and renewal settings.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.password.renewalDaysBeforeExpiry</code></td>
					<td>integer</td>
					<td></td>
					<td>e.g. <code>30</code></td>
					<td>RenewalDaysBeforeExpiry configures the days to renew the password before it gets expired.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.passwordSecret</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{key: password, name: &lt;Secret&gt;}</code></td>
					<td>Key is the data.key field within the secret containing the password value. If not specified, the secret will be automatically generated as <!-- raw HTML omitted -->-ssh-password.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.passwordSecret.key</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>password</code></td>
					<td>Key is the data.key field within the secret containing the password value. For Linux, this must be the hashed value that should be inserted into /etc/shadow.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.passwordSecret.name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>&lt;Secret&gt;</code></td>
					<td>Name is the name of the secret containing the password for the administrative account.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.requirePasswordOnSudo</code></td>
					<td>boolean</td>
					<td></td>
					<td>e.g. <code>true</code></td>
					<td>RequirePasswordOnSudo configures whether password re-authentication is required on sudo.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.sshAuthorizedKey</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>ssh-ed25519 AAAA... ops@admin</code></td>
					<td>The string of the SSH public key that is to be used for the administrative account. The public key must be of any FIPS-140 approved algorithm.</td>
			</tr>
			<tr>
					<td><code>osConfiguration.user.user</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vmware-system-user</code></td>
					<td>Name is the name of the user to be created. By default, this is vmware-system-user.</td>
			</tr>
			<tr>
					<td><code>resourceConfiguration</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{systemReserved: {cpu: 500m, memory: 1Gi}}</code></td>
					<td>ResourceConfiguration configures kubelet resource options. Currently, only CPU and memory reservations are supported.</td>
			</tr>
			<tr>
					<td><code>resourceConfiguration.systemReserved</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{cpu: 500m, memory: 1Gi}</code></td>
					<td>SystemReserved defines the system reserved CPU and memory reservations.</td>
			</tr>
			<tr>
					<td><code>resourceConfiguration.systemReserved.automatic</code></td>
					<td>boolean</td>
					<td></td>
					<td>default <code>true</code></td>
					<td>Automatic controls the automatic calculation of system reserved resources.</td>
			</tr>
			<tr>
					<td><code>resourceConfiguration.systemReserved.cpu</code></td>
					<td>int or string</td>
					<td></td>
					<td>e.g. <code>500m</code></td>
					<td>CPU describes the number of CPU cores reserved for system processes.</td>
			</tr>
			<tr>
					<td><code>resourceConfiguration.systemReserved.memory</code></td>
					<td>int or string</td>
					<td></td>
					<td>e.g. <code>1Gi</code></td>
					<td>Memory describes the memory resources reserved for system processes.</td>
			</tr>
			<tr>
					<td><code>storageClass</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>vsan-default-storage-policy</code></td>
					<td>StorageClass sets the StorageClass that will be used to create node root volumes.</td>
			</tr>
			<tr>
					<td><code>vmClass</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>best-effort-small</code></td>
					<td>VMClass sets the VMClass that will be used to create nodes. Supported scopes: cluster, controlPlane, workers</td>
			</tr>
			<tr>
					<td><code>volumes</code></td>
					<td>array of object</td>
					<td></td>
					<td>e.g. <code>[{name: containerd, capacity: 50Gi}]</code></td>
					<td>Volumes configures additional disks to be attached to node virtual machines. Supported scopes: cluster, controlPlane, workers</td>
			</tr>
			<tr>
					<td><code>volumes[].capacity</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>50Gi</code></td>
					<td>Capacity defines the storage capacity of the volume.</td>
			</tr>
			<tr>
					<td><code>volumes[].mountPath</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>/var/lib/containerd</code></td>
					<td>MountPath defines the mount path for the volume.</td>
			</tr>
			<tr>
					<td><code>volumes[].name</code></td>
					<td>string</td>
					<td>yes</td>
					<td>e.g. <code>containerd</code></td>
					<td>Name defines the name of the volume.</td>
			</tr>
			<tr>
					<td><code>volumes[].storageClass</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vsan-default-storage-policy</code></td>
					<td>StorageClass defines the Storage class to use for the volume.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{persistentVolumes: {availableStorageClasses: [vsan-default-storage-policy], ...}}</code></td>
					<td>VSphereOptions configures vSphere specific options related to nodes Supported scopes: cluster, controlPlane, workers</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes</code></td>
					<td>object</td>
					<td></td>
					<td>e.g. <code>{availableStorageClasses: [vsan-default-storage-policy], ...}</code></td>
					<td>PersistentVolumes configures what is available for PVCs to be used in the cluster.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes.availableStorageClasses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[vsan-default-storage-policy]</code></td>
					<td>AvailableStorageClasses lists the storage classes that can be used in the cluster.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes.availableVolumeSnapshotClasses</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[volumesnapshotclass-delete]</code></td>
					<td>AvailableVolumeSnapshotClasses lists the volume snapshot classes that can be used in the cluster.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes.customizableStorageClassAnnotations</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;annotation&gt;]</code></td>
					<td>CustomizableStorageClassAnnotations is a list of annotation keys set on the storage classes within the cluster which can be customized by the user.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes.customizableStorageClassLabels</code></td>
					<td>array of string</td>
					<td></td>
					<td>e.g. <code>[&lt;label&gt;]</code></td>
					<td>CustomizableStorageClassLabels is a list of label keys set on the storage classes within the cluster which can be customized by the user.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes.defaultStorageClass</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>vsan-default-storage-policy</code></td>
					<td>DefaultStorageClass sets the default storage class inside the cluster.</td>
			</tr>
			<tr>
					<td><code>vsphereOptions.persistentVolumes.defaultVolumeSnapshotClass</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>volumesnapshotclass-delete</code></td>
					<td>DefaultVolumeSnapshotClass sets the default volume snapshot class inside the cluster.</td>
			</tr>
	</tbody>
</table>

</details></p>

<p>Recipe, one control plane node and one worker, as we deployed it (ready in
four minutes):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">k8s</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">cluster.x-k8s.io/v1beta1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Cluster</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dev-01}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">clusterNetwork</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">pods</span><span class="p">:</span><span class="w"> </span>{<span class="nt">cidrBlocks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">192.168.156.0</span><span class="l">/20]}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">services</span><span class="p">:</span><span class="w"> </span>{<span class="nt">cidrBlocks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">10.96.0.0</span><span class="l">/12]}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">serviceDomain</span><span class="p">:</span><span class="w"> </span><span class="l">cluster.local</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">topology</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">class</span><span class="p">:</span><span class="w"> </span><span class="l">builtin-generic-v3.6.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">classNamespace</span><span class="p">:</span><span class="w"> </span><span class="l">vmware-system-vks-public</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v1.35.5+vmware.1-vkr.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">controlPlane</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">workers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">machineDeployments</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="nt">class</span><span class="p">:</span><span class="w"> </span><span class="l">node-pool</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">np-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vmClass</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">best-effort-small</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">storageClass</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="l">vsan-default-storage-policy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">osConfiguration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">value</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">ntp</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="nt">servers</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">172.30.0.34</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">wait</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">conditions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Ready</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">status</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;True&#34;</span><span class="w">
</span></span></span></code></pre></div><p>The same cluster in <code>v1beta2</code>, the version the Supervisor recommends; the
class becomes a reference with its namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">cluster.x-k8s.io/v1beta2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Cluster</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">dev-01}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">clusterNetwork</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">pods</span><span class="p">:</span><span class="w"> </span>{<span class="nt">cidrBlocks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">192.168.156.0</span><span class="l">/20]}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">services</span><span class="p">:</span><span class="w"> </span>{<span class="nt">cidrBlocks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="m">10.96.0.0</span><span class="l">/12]}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">serviceDomain</span><span class="p">:</span><span class="w"> </span><span class="l">cluster.local</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">topology</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">classRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">builtin-generic-v3.6.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">vmware-system-vks-public</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">v1.35.5+vmware.1-vkr.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">controlPlane</span><span class="p">:</span><span class="w"> </span>{<span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span>}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">workers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">machineDeployments</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- {<span class="nt">class</span><span class="p">:</span><span class="w"> </span><span class="nt">node-pool, name</span><span class="p">:</span><span class="w"> </span><span class="nt">np-1, replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span>}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">vmClass, value</span><span class="p">:</span><span class="w"> </span><span class="l">best-effort-small}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">storageClass, value</span><span class="p">:</span><span class="w"> </span><span class="l">vsan-default-storage-policy}</span><span class="w">
</span></span></span></code></pre></div><p><strong>Status worth reading:</strong> <code>status.phase</code>, <code>status.conditions</code>; the kubeconfig
is in the namespace as the Secret <code>&lt;cluster&gt;-kubeconfig</code> (ours:
<code>dev-01-kubeconfig</code>).</p>
<p><strong>Gotchas</strong></p>
<ul>
<li>Every namespace gets copies of a few ClusterClasses, on f06
<code>builtin-generic-v3.1.0</code> to <code>v3.3.0</code>; Broadcom&rsquo;s ClusterClass matrix marks
<code>v3.3.0</code> deprecated for VKS 3.6 and 3.7, and Broadcom&rsquo;s own 9.1 sample still
uses it. The newer classes live only in <code>vmware-system-vks-public</code>: name
that namespace (<code>classNamespace</code>, or <code>classRef.namespace</code> in <code>v1beta2</code>) to
use them.</li>
<li>The palette&rsquo;s <code>v1beta1</code> works, with a deprecation warning.</li>
<li>Nodes run Photon OS unless the cluster carries the annotation
<code>run.tanzu.vmware.com/resolve-os-image: os-name=ubuntu</code>.</li>
<li><code>topology.version</code> must be a release the Supervisor lists as ready and
compatible; on f06 those were <code>v1.32.x</code> to <code>v1.35.5</code>.</li>
<li>VKS needs the VPC&rsquo;s load balancer for the cluster&rsquo;s API endpoint, so the
namespace must use a VPC that has one.</li>
</ul>
<h2 id="utilpasswordentry">Util.PasswordEntry</h2>
<p><code>type: Util.PasswordEntry</code>. Not in the palette, but one of the five types: it
generates a password, or takes one you give it, and hashes it at request
time.</p>
<table>
	<thead>
			<tr>
					<th>Property</th>
					<th>Notes</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>length</code></td>
					<td>Generate a password of this length. Default 14.</td>
			</tr>
			<tr>
					<td><code>password</code></td>
					<td>A password to use instead, when <code>length</code> is not set.</td>
			</tr>
			<tr>
					<td><code>generatedPassword</code></td>
					<td>Computed: the generated password.</td>
			</tr>
			<tr>
					<td><code>sha512crypt</code></td>
					<td>Computed: the password&rsquo;s SHA-512 crypt hash (<code>$6$...</code>).</td>
			</tr>
	</tbody>
</table>


<p><details >
  <summary markdown="span">Every field the platform accepts (3)</summary>
  <table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Type</th>
					<th>Req.</th>
					<th>Values</th>
					<th>Description</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>count</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>1</code></td>
					<td>The number of resource instances to be created.</td>
			</tr>
			<tr>
					<td><code>length</code></td>
					<td>integer</td>
					<td></td>
					<td>default <code>14</code></td>
					<td>Length of the password to be auto generated</td>
			</tr>
			<tr>
					<td><code>password</code></td>
					<td>string</td>
					<td></td>
					<td>e.g. <code>${input.adminPassword}</code></td>
					<td>Password value received as an input when length is not specified</td>
			</tr>
	</tbody>
</table>

</details></p>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pw</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Util.PasswordEntry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">length</span><span class="p">:</span><span class="w"> </span><span class="m">20</span><span class="w">
</span></span></span></code></pre></div><p>VCF Automation stores both computed values as encrypted secrets
(<code>((secret:v1:...))</code>), so the deployment doesn&rsquo;t show them. Where the hash is
used decides how to write it:</p>
<ul>
<li>
<p>In a raw cloud-config held in a Secret, it is a string:
<code>hashed_passwd: ${resource.pw.sha512crypt}</code>.</p>
</li>
<li>
<p>In a VM&rsquo;s inline <code>cloudConfig</code> it must be a Secret reference, so put it in
a Secret first:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">webPw</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.ns.id}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-pw}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">Opaque</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">stringData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">ops-passwd</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.pw.sha512crypt}</span><span class="w">
</span></span></span></code></pre></div></li>
</ul>
<h2 id="complete-tested-blueprints">Complete, tested blueprints</h2>
<p>The five blueprints we deployed to test this guide, as they ran:</p>
<table>
	<thead>
			<tr>
					<th>File</th>
					<th>What it builds</th>
					<th>Result</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>test1-vpc.yaml</code></td>
					<td>A VPC, its attachment, an external IP allocation, a group, a gateway firewall policy referencing the group, a namespace in the new VPC, a generated password, two counted Secrets holding its hash</td>
					<td>Created in 2 min 15 s; every VPC object <code>Realized</code></td>
			</tr>
			<tr>
					<td><code>test1b-nat.yaml</code></td>
					<td>A DNAT rule on that VPC</td>
					<td>Created; NSX realized it without the port</td>
			</tr>
			<tr>
					<td><code>test3-workload.yaml</code></td>
					<td>In an existing VPC with a load balancer: a namespace, a subnet, a PVC, a VM group with a boot order, two Ubuntu VMs (cloud-init user with key and hashed password, data disk, subnet, anti-affinity), a LoadBalancer service</td>
					<td>Created in 2 min; SSH through the load balancer as the cloud-init user</td>
			</tr>
			<tr>
					<td><code>test4-vks.yaml</code></td>
					<td>A VKS cluster, one control plane node and one worker, <code>builtin-generic-v3.6.0</code></td>
					<td>Ready in 4 min</td>
			</tr>
			<tr>
					<td><code>test5-ipwait.yaml</code></td>
					<td>One VM whose output is its IP</td>
					<td>Output <code>172.30.0.2</code></td>
			</tr>
	</tbody>
</table>
<h2 id="downloads">Downloads</h2>
<ul>
<li><a href="/files/vcfa-91-blueprint-reference.zip"><code>vcfa-91-blueprint-reference.zip</code></a>:
the five test blueprints; the five base types as VCF Automation&rsquo;s API
returns them; the fifteen palette schemas from the designer and the palette
map; and the field tables of this guide as Markdown.</li>
</ul>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>Self-service on VCF Automation All Apps is only as good as its blueprints.
And a blueprint is only as good as its author&rsquo;s knowledge of the fields,
which are mostly documented somewhere else, or nowhere.</p>
<p>The cost of not knowing shows up late. The designer saves the blueprint,
the validator passes it, and the request fails ten minutes in. Or worse, it
succeeds, with a NAT rule that forwards every port or a firewall rule that
allows any service.</p>
<p>Knowing what the platform actually enforces turns that into a five-second
dry run. It also turns a catalog item from a demo into something a team can
depend on.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>The palette is five resource types. Learn <code>CCI.Supervisor.Resource</code> and
<code>CCI.VPC.Configuration</code> and you can write every item by hand, including
kinds the palette doesn&rsquo;t show.</li>
<li>VCF Automation&rsquo;s validation checks a resource&rsquo;s own properties, never the
manifest or the VPC spec inside. Dry-run manifests against the Supervisor;
test VPC objects by deploying them.</li>
<li>Where the designer&rsquo;s form and the platform disagree, the platform wins:
<code>accessModes</code>, <code>labelSelector</code>, VM affinity terms ending
<code>PreferredDuringExecution</code>.</li>
<li>Outputs are computed once. Wait for what they read: a VM&rsquo;s address needs
the condition <code>VirtualMachineGuestNetworkConfigSynced</code>.</li>
<li>A blueprint VPC has no load balancer and can&rsquo;t get one, so LoadBalancer
services and VKS need a VPC made in the UI.</li>
<li>Name firewall services (<code>&quot;:HTTPS&quot;</code>) instead of port sets, give every rule a
<code>from</code>, and treat a NAT rule as mapping the whole address.</li>
<li>Inline cloud-init passwords are Secret references; <code>count.index</code> needs
<code>allocatePerInstance: true</code>.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation.html">Managing Blueprints in VCF Automation</a>: blueprints, the designer, inputs, versions, property groups and custom forms.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/sample-blueprints-in-vcf-automation-for-all-apps.html">Sample Blueprints in VCF Automation</a>: Broadcom&rsquo;s samples: VMs, <code>count</code> with <code>allocatePerInstance</code>, a VM with a VKS cluster, a VPC with a namespace, a VM group with affinity.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/specifying-formatversion-in-your-blueprints.html">Specifying formatVersion in Blueprints</a>: what <code>formatVersion: 2</code> adds, including outputs and <code>__deploymentOverview</code>.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/bindings-and-dependencies.html">Creating bindings and dependencies between resources</a>: <code>dependsOn</code> and property bindings, and how each orders the build.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/property-groups/input-property-groups.html">Input Property Groups</a> and <a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/property-groups/constant-property-groups-in-vcf-automation-for-all-apps.html">Constant Property Groups</a>: <code>${input.&lt;group&gt;.&lt;property&gt;}</code> and <code>${propgroup.&lt;group&gt;.&lt;property&gt;}</code>.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/administering-all-apps-organizations-in-vcfa-automation/managing-secrets-in-vcfa.html">Managing Secrets in VCF Automation</a>: <code>${secret.&lt;name&gt;}</code>, organization and project secrets.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/preparing-for-day-2.html">VCF Automation blueprint designs that prepare for day 2 changes</a>: re-applying a blueprint versus day-2 actions, and bindings in day 2.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-projects-in-vcfa/create-a-namespace-class.html">Create a Namespace Class in VCF Automation</a>: what a namespace class sets, and so what a blueprint namespace must add.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/adding-and-managing-virtual-private-clouds/add-a-vpc.html">Create a Virtual Private Cloud in VCF Automation</a>: a VPC&rsquo;s connectivity profile, private CIDRs and load balancing, and that VKS needs load balancing.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/adding-and-managing-virtual-private-clouds/add-a-vpc/create-a-nat-rule-for-a-vpc-in-vcf-automation(1).html">Create a NAT Rule for a VPC in VCF Automation</a>: the NAT actions, external addresses and priorities behind <code>VPCNATRule</code>.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-security-load-balancing/vdefend/vdefend-firewall/9-1/vcf-automation-integration-with-vdefend-firewall/security-management-workflow.html">Secure North-South boundaries for Transit Gateways and VPCs (vDefend 9.1)</a>: VPC gateway firewall policies, rules realized on the edges, and the activation flag in the security profile.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-service-administration-and-development/9-1/provision-and-manage-virtual-machines/deploying-and-managing-virtual-machines-in-vsphere-iaas-control-plane.html">Deploying and Managing Virtual Machines in vSphere Supervisor</a>: VM classes, images, storage classes and zones, with a pointer to the VM Operator API.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/provisioning-tkg-service-clusters/using-the-cluster-v1beta1-api/using-the-versioned-clusterclass.html">Using the Versioned ClusterClass</a>: the ClusterClass matrix per VKS release and <code>vmware-system-vks-public</code>.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/provisioning-tkg-service-clusters/using-the-cluster-v1beta1-api/using-the-versioned-clusterclass/v1beta1-example-default-cluster.html">v1beta1/v1beta2 Example: Default Cluster</a>: the minimum cluster and the CIDR rules.</li>
<li><a href="https://developer.broadcom.com/xapis/vmware-vsphere-kubernetes-service/3.7.0/variable-docs.html">ClusterClass Variable Reference</a>: every variable of the builtin-generic classes, and where each can be overridden.</li>
<li><a href="https://knowledge.broadcom.com/external/article/435137/vm-status-information-missing-in-vcf-aut.html">VM Status Information Missing in VCF Automation 9.0.x Deployments (KB 435137)</a>: where the designer&rsquo;s default VM <code>wait</code> comes from.</li>
</ul>
<p>The VM Operator API itself is documented upstream, outside Broadcom, and
Broadcom&rsquo;s VM Service pages link there: <a href="https://vm-operator.readthedocs.io/en/latest/ref/api/v1alpha5/">v1alpha5 reference</a>.</p>
<hr>
<p><em>Lab environment; opinions my own. Every snippet was validated, dry-run or
deployed on a live VCF 9.1 environment; the field tables come from the
platform&rsquo;s own schemas.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>What&#39;s a VPC? Let Pac-Man explain</title>
      <link>https://thenestedlab.com/posts/whats-a-vpc-with-pacman/</link>
      <pubDate>Wed, 16 Sep 2026 08:30:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/whats-a-vpc-with-pacman/</guid>
      <description>Part 0 of the Pod Papers: an NSX VPC explained with a running game of Pac-Man. Private by default, one deliberate door out, and a self-inflicted outage where five green layers hid one wrong integer.</description>
      <content:encoded><![CDATA[<p>Before this series gets into trunk subnets and binding maps, it&rsquo;s worth ten
minutes on the thing everything else stands on: <strong>what an NSX VPC actually
is</strong>, seen from the tenant&rsquo;s chair. No slides, just a game of Pac-Man.
Strictly for educational purposes, you understand.</p>
<p>The lab has Pac-Man running twice on VCF 9.1. One copy runs on a VKS
(vSphere Kubernetes Service) cluster I built by hand with <code>kubectl</code>. The
other runs on a cluster deployed through VCF Automation&rsquo;s catalog. Both live
inside VPCs, and both were reachable when I started:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">http://192.168.144.15/  -&gt;  &lt;title&gt;Pacman in HTML 5 Canvas
</span></span><span class="line"><span class="cl">http://192.168.144.23/  -&gt;  &lt;title&gt;Pacman in HTML 5 Canvas
</span></span></code></pre></div><figure class="nl-video">
  <video autoplay loop muted playsinline controls preload="metadata" style="aspect-ratio:710 / 610">
    <source src="/images/pacman-vip-23.mp4" type="video/mp4">
  </video>
  <figcaption>Pac-Man, live at <code>192.168.144.23</code> — a VIP on the VPC load balancer. The only door in.</figcaption>
</figure>

<h2 id="a-vpc-is-a-private-universe-with-a-door-policy">A VPC is a private universe with a door policy</h2>
<p>Think of an NSX VPC as a tenant&rsquo;s own routed network space. It has its own
subnets, its own gateway and its own address plan. The tenant carves it out,
rather than filing a ticket with the network team. Three rules define it:</p>
<ol>
<li><strong>Private by default.</strong> A <code>Private</code> subnet is reachable only from inside
the same VPC. Nobody outside can route to it: not other tenants, not
other VPCs in the same org, not the corporate network.</li>
<li><strong>Your addresses are your business.</strong> Private subnets aren&rsquo;t advertised
anywhere, so two VPCs can use <em>identical</em> CIDRs. (This is the superpower
the rest of the series is built on.)</li>
<li><strong>Every door out is deliberate.</strong> Traffic leaves through the transit
gateway (with source NAT), or arrives through a <strong>LoadBalancer VIP</strong> from
an external block the provider allocated. Nothing is exposed by accident.</li>
</ol>
<p>Pac-Man&rsquo;s pods sit on a private subnet. The only reason <code>192.168.144.23</code>
answers is a Kubernetes <code>Service</code> of type <code>LoadBalancer</code>, which NSX turns
into a VIP on the VPC&rsquo;s load balancer. So let&rsquo;s remove the door.</p>
<h2 id="before-close-the-door">Before: close the door</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl patch svc pacman -n pacman -p &#39;{&#34;spec&#34;:{&#34;type&#34;:&#34;ClusterIP&#34;}}&#39;
</span></span><span class="line"><span class="cl">service/pacman patched
</span></span><span class="line"><span class="cl">NAME     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
</span></span><span class="line"><span class="cl">pacman   ClusterIP   10.106.219.213   &lt;none&gt;        80/TCP    4d13h
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ curl -m 5 http://192.168.144.23/
</span></span><span class="line"><span class="cl">curl: timed out / unreachable
</span></span></code></pre></div><p>The pods are running. The service exists. The game is fine, <em>for anything
inside the VPC</em>. From my desk, it has simply gone, ghosts and all.</p>
<p>That&rsquo;s the whole VPC model in one <code>curl</code>. The boundary isn&rsquo;t a firewall rule
somebody wrote; it&rsquo;s the absence of a route.</p>
<h2 id="after-open-it-again">After: open it again</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl patch svc pacman -n pacman -p &#39;{&#34;spec&#34;:{&#34;type&#34;:&#34;LoadBalancer&#34;}}&#39;
</span></span><span class="line"><span class="cl">service/pacman patched
</span></span><span class="line"><span class="cl">NAME     TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
</span></span><span class="line"><span class="cl">pacman   LoadBalancer   10.106.219.213   192.168.144.23   80:31467/TCP   4d13h
</span></span></code></pre></div><p>Same VIP, handed straight back. NSX programmed a virtual server and pool on
the VPC&rsquo;s load balancer, and the supervisor stitched them to the cluster&rsquo;s
NodePort. Door open. Lesson over, or so I thought.</p>
<p>And then the game <em>didn&rsquo;t load</em>.</p>
<h2 id="the-outage-i-gave-myself-this-is-the-useful-bit">The outage I gave myself (this is the useful bit)</h2>
<p>Everything was green:</p>
<ul>
<li><code>kubectl get svc</code>: LoadBalancer, VIP assigned</li>
<li><code>kubectl get endpoints</code>: pod IPs present</li>
<li>NSX: virtual server up, pool members healthy</li>
<li><code>iptables</code> on the node: NodePort rules identical to a working
neighbour service</li>
</ul>
<p>Five layers, all green, and <code>curl</code> hung. The control experiment was the
Pac-Man at <code>.15</code> on the hand-built cluster: untouched throughout, and still
playing.</p>
<p>The culprit, it turns out, was me. My &ldquo;harmless&rdquo; <code>ClusterIP</code> patch earlier
had included a <code>ports</code> list. A merge patch with <code>kubectl patch</code> <strong>replaces
arrays</strong> rather than merging them, and my array said <code>targetPort: 80</code>.
Pac-Man listens on <strong>8080</strong>.</p>
<p>So every layer above was faithfully forwarding traffic to a port nothing was
listening on. And every layer reported success, because <em>its</em> job was done.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl patch svc pacman -n pacman --type=json \
</span></span><span class="line"><span class="cl">    -p &#39;[{&#34;op&#34;:&#34;replace&#34;,&#34;path&#34;:&#34;/spec/ports/0/targetPort&#34;,&#34;value&#34;:8080}]&#39;
</span></span><span class="line"><span class="cl">service/pacman patched
</span></span><span class="line"><span class="cl">$ curl -s http://192.168.144.23/ | grep -o &#39;&lt;title&gt;.*&lt;/title&gt;&#39;
</span></span><span class="line"><span class="cl">&lt;title&gt;Pacman in HTML 5 Canvas&lt;/title&gt;
</span></span></code></pre></div><p>Instant recovery. The diagnosis walked the whole paravirtual chain: the VIP,
the supervisor&rsquo;s <code>VirtualMachineService</code>, the NSX virtual server and pool,
the NodePort <code>iptables</code> rules, and finally the pod. It&rsquo;s exactly the walk
you&rsquo;ll need one day:</p>
<table>
	<thead>
			<tr>
					<th>Layer</th>
					<th>Check</th>
					<th>What &ldquo;green&rdquo; hides</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>VIP</td>
					<td><code>kubectl get svc</code> EXTERNAL-IP</td>
					<td>nothing about the backend</td>
			</tr>
			<tr>
					<td>NSX LB</td>
					<td>virtual server + pool status</td>
					<td>pool health is TCP to the <em>NodePort</em>, not the pod</td>
			</tr>
			<tr>
					<td>Endpoints</td>
					<td><code>kubectl get endpoints</code></td>
					<td>it lists pod IP:<strong>targetPort</strong> — read the number</td>
			</tr>
			<tr>
					<td>Node</td>
					<td><code>iptables -t nat -L KUBE-SERVICES</code></td>
					<td>rules can be perfect and point at the wrong port</td>
			</tr>
			<tr>
					<td>Pod</td>
					<td><code>kubectl exec ... ss -ltn</code></td>
					<td>the only place the truth lives</td>
			</tr>
	</tbody>
</table>
<p>Bonus find on the way: kube-proxy <em>and</em> Antrea on that cluster had dropped
their API watches days earlier (<code>http2: client connection lost</code>). Neither
re-established its informers until it was restarted. It didn&rsquo;t cause this
outage, but it&rsquo;s the kind of thing you only find when you&rsquo;re forced to look.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>If you run a platform for more than one team, this is the feature you&rsquo;ve been
asking the network team for. A VPC gives each team, project or customer its
own private network space. They create it themselves, in minutes, and nothing
in it is reachable from outside until they publish it.</p>
<p>Security teams like it for the same reason developers do. Exposure is a
deliberate, auditable act, not a side effect of plugging something in.</p>
<p>What organisations do with it once they have it:</p>
<ul>
<li><strong>Per-team sandboxes</strong> that can&rsquo;t see each other, provisioned without a
ticket.</li>
<li><strong>Partner or supplier environments</strong>, isolated from the corporate estate
but hosted on the same platform.</li>
<li><strong>Multi-tenant hosting</strong>, for service providers and internal IT alike,
with isolation enforced by topology rather than a growing pile of firewall
rules.</li>
</ul>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>A VPC&rsquo;s boundary is <strong>the absence of a route</strong>, not a rule. <code>Private</code>
subnets are unreachable from outside by construction, which is also why
identical CIDRs across VPCs just work.</li>
<li>A <code>LoadBalancer</code> service is the <em>deliberate</em> door: an NSX VIP from the
external block, programmed per service. Flip the type and the door closes,
with nothing else to clean up.</li>
<li><code>kubectl patch</code> (merge) <strong>replaces <code>spec.ports</code></strong>; it doesn&rsquo;t merge it.
Patch a single field with <code>--type=json</code>, or leave the array out.</li>
<li>Five green layers can hide one wrong integer. Keep a <em>working control</em>
(here, the untouched <code>.15</code> instance) and compare layer by layer.</li>
<li>Read <code>kubectl get endpoints</code> as <code>IP:targetPort</code>. The port is the part
people skim.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview.html">Virtual Private Clouds Overview</a>: the subnet access modes, and the NAT a private subnet needs to reach outside.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/create-a-vpc.html">Create a VPC</a>: private CIDRs, local to each VPC and allowed to overlap between VPCs.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/transit-gateways.html">Transit Gateways</a>: how VPCs reach each other and the outside network.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/vsphere-supervisor-installation-and-configuration/supervisor-networking-with-virtual-private-clouds.html">Deploying Supervisor with VCF Networking with VPC</a>: the VPC, load balancer and SNAT IP behind a namespace, and the LoadBalancer services NCP provides.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/vsphere-supervisor-installation-and-configuration/configuring-and-managing-vsphere-namespaces/managing-vsphere-namespaces-on-a-supervisor-with-nsx-vpc/create-and-configure-a-vsphere-namespace-on-a-supervisor-with-vpc/create-namespaces-with-vpc-nosnat-nolb.html">Create vSphere Namespaces on VPCs without SNAT and Load Balancer</a>: without the VPC load balancer, LoadBalancer services cannot be deployed at all.</li>
</ul>
<p><em>Next in the Pod Papers: <a href="/posts/nested-esxi-nsx-vpc/">nested ESXi inside a VPC</a>,
where &ldquo;private by default&rdquo; meets a host that fakes its own MAC address.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Output captured live, trimmed for length,
never edited for outcome — including the outage.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Nested ESXi inside an NSX VPC: the trunk-subnet design</title>
      <link>https://thenestedlab.com/posts/nested-esxi-nsx-vpc/</link>
      <pubDate>Wed, 16 Sep 2026 08:20:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/nested-esxi-nsx-vpc/</guid>
      <description>Plain VPC subnets silently blackhole a nested ESXi host. Why, and the trunk subnet and binding map design that makes nested labs work as an ordinary NSX VPC tenant.</description>
      <content:encoded><![CDATA[<p>The host booted clean. Management IP configured, services up, DCUI happy.
And every single packet it sent, ARP included, died silently. The host was
having a lovely time; it just couldn&rsquo;t tell anyone.</p>
<p>That&rsquo;s how my first attempt at running nested ESXi inside an NSX VPC ended.
The failure is nasty precisely because nothing <em>looks</em> wrong. If you&rsquo;re
building nested vSphere labs on VCF 9 with VPC networking, this post is the
map of the minefield. It&rsquo;s also the design that gets you across it, verified
live.</p>
<h2 id="the-setup">The setup</h2>
<p>VCF 9.1, with the vSphere Supervisor on NSX VPC networking. The goal was to
deploy nested ESXi hosts as ordinary VM Service VMs inside a tenant&rsquo;s VPC.
No physical fabric changes, no provider tickets, no special treatment. It&rsquo;s
the kind of thing you want for training pods, cert-study labs, or
reproducing customer issues.</p>
<p>Nested ESXi needs what physical ESXi needs: a management network, vMotion
and vSAN. Traditionally, those are VLANs trunked to every host. But a VPC is
an overlay world, and there are no VLANs to trunk. So what happens if you
just attach the nested host&rsquo;s vNIC to a normal VPC subnet?</p>
<h2 id="failure-1-the-silent-blackhole">Failure #1: the silent blackhole</h2>
<p>Here&rsquo;s the trap. A standard VPC subnet port gets <strong>address bindings</strong>. NSX
pins the exact IP and MAC it allocated to that vNIC, and SpoofGuard drops
everything else.</p>
<p>ESXi&rsquo;s vmk0 doesn&rsquo;t use the vNIC&rsquo;s MAC. It makes up its own:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">vmk0
</span></span><span class="line"><span class="cl">   MAC Address: 00:50:ac:1e:00:8c     &lt;- NOT the vNIC MAC (04:50:56:...)
</span></span></code></pre></div><p>So every frame the management interface sends carries a MAC the port doesn&rsquo;t
own. NSX drops all of it (ARP, ping, everything), while the host itself boots
green and reports healthy. There&rsquo;s no error anywhere, which is somehow worse
than a bad one. You just can&rsquo;t reach it, ever.</p>
<p><img alt="Standard VPC subnet port: SpoofGuard pins one IP+MAC; vmk0&rsquo;s synthesised MAC loses, silently" loading="lazy" src="/images/post1-blackhole.svg"></p>
<p>(There&rsquo;s a second trap stacked on top. Our VPC subnets run with DHCP
deactivated, so the appliance also sits at &ldquo;waiting for DHCP&rdquo; unless you
inject static addressing through OVF <code>guestinfo.*</code> properties. NSX can give a
VPC subnet a DHCP server or relay (<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-subnet-for-the-vpc.html">Add a Subnet to a VPC</a>);
ours had neither. More on that below.)</p>
<h2 id="the-design-that-works-a-trunk-subnet--binding-maps">The design that works: a trunk subnet + binding maps</h2>
<p>The fix isn&rsquo;t a hack. It&rsquo;s a first-class NSX VPC construct that&rsquo;s barely
documented in the wild: <strong><code>SubnetConnectionBindingMap</code></strong>.</p>
<p>The idea:</p>
<ol>
<li>Create one ordinary VPC subnet to act as a <strong>trunk</strong> (<code>sn-trunk</code>). The
nested host&rsquo;s vNICs attach <em>only</em> here.</li>
<li>Create a normal VPC subnet for each traditional network: <code>sn-mgmt</code>,
<code>sn-vmotion</code> and <code>sn-vsan</code>.</li>
<li>Bind each of those to the trunk with a <strong>binding map carrying a VLAN tag</strong>.
The nested host&rsquo;s vSwitch tags frames exactly as it would on metal. The
binding map strips the tag and delivers the frame into the right subnet.</li>
</ol>
<p>It&rsquo;s pure L2 demultiplexing. One vNIC carries N VLANs, and the VPC never
routes on a tag. The physical fabric never sees any of it, because the
802.1Q header rides inside the Geneve overlay.</p>
<p>A tenant can create all of it through the supervisor, as Kubernetes objects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># sn-trunk and sn-mgmt are ordinary Private Subnets; the interesting object:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">crd.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">SubnetConnectionBindingMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">bm-mgmt}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">subnetName</span><span class="p">:</span><span class="w"> </span><span class="l">sn-mgmt         </span><span class="w"> </span><span class="c"># the map is a child of the VLAN subnet...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">targetSubnetName</span><span class="p">:</span><span class="w"> </span><span class="l">sn-trunk  </span><span class="w"> </span><span class="c"># ...and points AT the trunk</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vlanTrafficTag</span><span class="p">:</span><span class="w"> </span><span class="m">1610</span><span class="w">
</span></span></span></code></pre></div><p>That direction is easy to get backwards, so it&rsquo;s worth saying twice: <strong>the
binding map belongs to the VLAN subnet and points at the trunk</strong>, not the
other way round.</p>
<p><img alt="NSX: sn-trunk realized once per VPC, binding maps hanging off the VLAN subnets" loading="lazy" src="/images/ui/u11b-nsx-sntrunk-per-vpc.jpg"></p>
<p>On the nested host, nothing exotic: plain virtual switch tagging (VST), just
like physical. It never suspects a thing.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Name                Virtual Switch  Active Clients  VLAN ID
</span></span><span class="line"><span class="cl">------------------  --------------  --------------  -------
</span></span><span class="line"><span class="cl">Management Network  vSwitch0                     1     1610
</span></span><span class="line"><span class="cl">vMotion             vSwitch0                     1     1611
</span></span><span class="line"><span class="cl">vSAN                vSwitch0                     1     1612
</span></span></code></pre></div><p><img alt="Host Client: port groups on VLANs 1610 / 1611 / 1612" loading="lazy" src="/images/ui/u12a-hostclient-portgroups-vlans.jpg">
<em>The same three VLANs as the nested host sees them.</em></p>
<p>And because there&rsquo;s no DHCP in our VPC subnets, the nested-ESXi appliance
gets its identity through OVF properties in the VM Service spec:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vAppConfig</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.ipaddress, value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;172.30.0.40&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.netmask,   value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;255.255.255.224&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.gateway,   value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;172.30.0.33&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.vlan,     value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;1610&#34;</span>}}<span class="w">
</span></span></span></code></pre></div><h2 id="does-it-actually-work-the-receipts">Does it actually work? The receipts</h2>
<p>Two nested hosts, vNICs on <code>sn-trunk</code>, three VLANs. From host one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[root@esx01:~] vmkping -c2 172.30.0.41            # mgmt, VLAN 1610
</span></span><span class="line"><span class="cl">3 packets transmitted, 3 packets received, 0% packet loss
</span></span><span class="line"><span class="cl">[root@esx01:~] vmkping -I vmk1 -c3 172.30.0.71    # vMotion, VLAN 1611
</span></span><span class="line"><span class="cl">3 packets transmitted, 3 packets received, 0% packet loss
</span></span><span class="line"><span class="cl">[root@esx01:~] vmkping -I vmk2 -c3 172.30.0.101   # vSAN, VLAN 1612
</span></span><span class="line"><span class="cl">3 packets transmitted, 3 packets received, 0% packet loss
</span></span></code></pre></div><p><img alt="Live capture: vmnic0 down, vMotion and vSAN VLANs still passing at 0% loss" loading="lazy" src="/images/demo-c6-nic-failover.jpg">
<em>The transcript that matters: fail the first NIC, and every VLAN keeps flowing on the second — captured live.</em></p>
<p>Two more results are worth knowing before you design around this.</p>
<p><strong>Untagged frames are dropped.</strong> I put a probe vmk on the untagged port
group, using the address NSX itself had allocated to the trunk port. The
result was 100% loss and an empty ARP table, while tagged traffic flowed
happily beside it, as if to make a point. Every network your nested host
uses needs a VLAN and a binding map. There is no untagged fallback.</p>
<p><strong>Failover behaves like real hardware.</strong> With two vNICs on the trunk, teamed
active/active, <code>esxcli network nic down -n vmnic0</code> moved every VLAN onto
vmnic1 with zero loss. The SSH session I was watching from never dropped.
A vmk MAC moving between trunk ports mid-flow is exactly what MAC-pinned
standard ports would blackhole. The trunk carries it fine.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>Running whole vSphere environments <em>inside</em> a VPC turns the platform into
something most customers never had. It&rsquo;s a way to stand up complete,
isolated copies of infrastructure on demand, with no physical fabric change
and no waiting for anyone. That&rsquo;s what makes it commercially interesting:</p>
<ul>
<li><strong>Training and certification labs</strong> where every learner gets a real
vSphere environment, not a shared one.</li>
<li><strong>Reproducing a customer problem</strong> on a like-for-like copy, rather than on
their own estate.</li>
<li><strong>Rehearsing upgrades and migrations</strong> end to end before the change
window, then throwing the copy away.</li>
<li><strong>Vendor and feature evaluations</strong> with real behaviour, at zero risk to
production.</li>
</ul>
<p>This is the design Comms-care uses to give every consultant a dedicated
environment. The same pattern scales to a classroom or a proof-of-concept
factory.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>A nested ESXi vNIC on a <strong>standard</strong> VPC subnet is dead on arrival:
vmk0&rsquo;s made-up MAC loses to SpoofGuard, silently.</li>
<li>Attach nested-host vNICs <strong>only to a trunk subnet</strong>, with one binding map
per VLAN. The map lives under the VLAN subnet and points at the trunk.</li>
<li><strong>No DHCP in our VPC subnets</strong>, so bootstrap addressing goes through
<code>guestinfo.*</code> (appliances) or cloud-init (Linux). Static IP plans are a
feature in a lab anyway.</li>
<li>ESXi&rsquo;s default TCP/IP stack has <strong>one</strong> gateway. Set per-vmk override
gateways (<code>esxcli ... ipv4 set -g</code>) so vMotion and vSAN use their own
subnet&rsquo;s gateway.</li>
<li>Recreating a VM <strong>reallocates</strong> its NSX addresses. Pin what you depend on.</li>
<li>MTU: everything here ran at 1500. Raise the trunk and the nested vDS
before you do vSAN at any real scale.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-service-administration-and-development/9-0/managing-vsphere-kuberenetes-service-clusters-and-workloads/managing-networking-for-tkg-service-clusters/enable-antrea-egress-separate-subnet-on-a-tkg-cluster-with-nsx-vpc/create-a-subnetconnectionbindingmap-cr-on-the-supervisor.html">Create a SubnetConnectionBindingMap CR on the Supervisor</a>: the binding map CR and its <code>vlanTrafficTag</code>, shown for VKS egress subnets.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/segments/creating-a-child-segment.html">Creating a Child Segment</a>: the NSX mechanism underneath: a binding map on the child segment, pointing at its parent with a VLAN ID.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/segments/segment-profiles/understanding-spoofguard-segment-profile.html">Understanding SpoofGuard Segment Profile</a>: port address bindings, and traffic dropped when its MAC or IP does not match them.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-vpc-service-profile.html">Add a VPC Service Profile</a>: the DHCP settings and segment profiles, SpoofGuard included, that a VPC&rsquo;s subnets inherit.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/vm-service/deploy-vms-with-configurable-ovf-properties-vsphere-iaas-control-plane.html">Deploy VMs with Configurable OVF Properties in vSphere Supervisor</a>: OVF properties set through the VM Service&rsquo;s vAppConfig transport.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/9-0/vsphere-networking/setting-up-vmkernel-networking/configure-the-vmkernel-adapter-gateway-by-using-esxcli.html">Configure the VMkernel Adapter Gateway by Using esxcli Commands</a>: a gateway per VMkernel adapter, set with esxcli.</li>
</ul>
<p>Next in this series: what happens when you want <em>ten</em> of these labs, with
byte-identical IP plans, firewalled from each other by construction. That&rsquo;s
where NSX VPCs go from &ldquo;workaround&rdquo; to genuinely better than physical.</p>
<hr>
<p><em>Lab environment; opinions my own. Everything above was captured from a live
VCF 9.1 environment — output trimmed for length, never edited for outcome.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>The load balancer that must exist before the namespace</title>
      <link>https://thenestedlab.com/posts/the-lb-that-must-exist-first/</link>
      <pubDate>Wed, 16 Sep 2026 08:10:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/the-lb-that-must-exist-first/</guid>
      <description>Addresses pending for ever, a retryable error that never stops retrying, and an ordering rule the docs don&amp;rsquo;t tell you: in a self-service NSX VPC, the load balancer must exist before the namespace that uses it.</description>
      <content:encoded><![CDATA[<p>Everything was green. The VPC: realized. The namespace: ready. The VMs:
powered on, endpoints populated, ports listening. And the LoadBalancer
services sat at <code>&lt;pending&gt;</code>. For an hour.</p>
<p>This is the story of the least helpful error message in my recent memory,
what it actually means, and the one-line ordering rule that would have saved
an afternoon. If you&rsquo;re doing self-service NSX VPCs on VCF 9 with the
vSphere Supervisor, you will hit this. Bookmark accordingly.</p>
<h2 id="the-setup">The setup</h2>
<p>The VPC was tenant-created, through VCF Automation&rsquo;s Cloud Consumption
Interface (CCI) API. A supervisor namespace was pinned to it, with a couple
of <code>VirtualMachineService</code> objects of type <code>LoadBalancer</code> to publish SSH and
HTTPS for the workloads inside. Standard stuff: the exact pattern that works
out of the box in the org&rsquo;s default VPC.</p>
<p>The Kubernetes side looked perfect:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ kubectl get endpoints -n pod-a
</span></span><span class="line"><span class="cl">NAME           ENDPOINTS                        AGE
</span></span><span class="line"><span class="cl">esx01-access   172.30.0.40:443,172.30.0.40:22   6m36s
</span></span><span class="line"><span class="cl">esx02-access   172.30.0.41:443,172.30.0.41:22   6m35s
</span></span></code></pre></div><p>Endpoints resolved. VIPs: nothing. The only clue was a recurring event:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Warning  FailedRealizeNSXResource  service/esx01-access
</span></span><span class="line"><span class="cl">Generic error occurred during realizing network for Service
</span></span></code></pre></div><p>&ldquo;Generic error.&rdquo; Wonderful.</p>
<h2 id="digging-what-ncp-actually-wants">Digging: what NCP actually wants</h2>
<p>The supervisor&rsquo;s network container plugin (NCP) told the real story in its
logs:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">nsx_ujo.ncp.nsx.policy.lb_layer4_service Lb Service not Found for Namespace pod-a
</span></span><span class="line"><span class="cl">NCP00270 Failed to process virtual ip for service ...: Lbs pod-a is not found
</span></span><span class="line"><span class="cl">Encountered retryable error ... : Lbs pod-a is not found
</span></span></code></pre></div><p>NCP wants an NSX <strong>LBService</strong> in the namespace&rsquo;s VPC. In the org&rsquo;s
<em>default</em> VPC, one exists, because the platform created it when the VPC was
born. In my self-service VPC? Nobody had created one.</p>
<p>Fair enough. That&rsquo;s actually documented behaviour, once you know where to
look. A fresh VPC needs a <code>LoadBalancer</code> object. Before that, it needs a
<code>VPCAttachment</code> to a connectivity profile with the service gateway enabled.
Without the attachment, the load balancer creation itself fails, with a much
better error message.</p>
<p>So I created the attachment, then the LBService. NSX: <code>Realized=True</code>.
Problem solved?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Warning  FailedRealizeNSXResource  service/esx01-access
</span></span><span class="line"><span class="cl">Generic error occurred during realizing network for Service
</span></span></code></pre></div><p>No.</p>
<h2 id="the-actual-bug-shaped-behaviour-a-snapshot-not-a-lookup">The actual bug-shaped behaviour: a snapshot, not a lookup</h2>
<p>Here&rsquo;s the part that costs you the afternoon. That &ldquo;retryable error&rdquo; retries
the <em>lookup in NCP&rsquo;s cache</em>, not the discovery. <strong>NCP snapshots the VPC&rsquo;s
load balancer inventory when the namespace is created.</strong> An LBService that
appears afterwards is never discovered, however long you wait:</p>
<ul>
<li>Recreating the Kubernetes services: no effect.</li>
<li>Tagging the LBService with the <code>nsx-op/*</code> ownership tags the working ones
carry: no effect, because the cache doesn&rsquo;t re-read NSX.</li>
<li>Restarting NCP would force a full resync, but supervisor system pods are
protected. Even <code>Administrator@vsphere.local</code> gets a Forbidden, which is a
humbling thing to read.</li>
<li>Mutating the namespace to nudge a resync: also blocked, by the
supervisor&rsquo;s namespace validation webhook.</li>
</ul>
<p>As a tenant, there is exactly one fix: <strong>delete and recreate the namespace</strong>,
now that its VPC has a load balancer. Fifteen minutes of rebuild, for want of
one ordering rule.</p>
<p>And the control experiment proves the rule. A namespace created <em>after</em> its
VPC already had an LBService got its VIPs assigned without any drama:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">esx01-access   VIP=192.168.144.34   22 OPEN · 443 OPEN
</span></span><span class="line"><span class="cl">esx02-access   VIP=192.168.144.35   22 OPEN · 443 OPEN
</span></span></code></pre></div><p>Once the ordering is right, this is what &ldquo;working&rdquo; looks like. It&rsquo;s the
pod&rsquo;s state a couple of minutes after a correctly ordered deployment:</p>
<p><img alt="Live replay: catalog-deployed pod with both VMs powered on and VIPs assigned" loading="lazy" src="/images/c2-catalog-pod.gif"></p>
<p><img alt="VCFA deployment topology: namespace, subnets, hosts, two VIPs" loading="lazy" src="/images/ui/u4-deployment-topology.jpg">
<em>What the requester sees once the order is right.</em></p>
<h2 id="the-ordering-rule">The ordering rule</h2>
<p>For every self-service VPC that will publish LoadBalancer services, create
these in order, <em>before</em> the namespace:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">1. VPC                                   (vpc.nsx.vmware.com/v1alpha1)
</span></span><span class="line"><span class="cl">2. VPCAttachment                         (connectivity profile w/ service gateway
</span></span><span class="line"><span class="cl">                                          — LB creation errors without it)
</span></span><span class="line"><span class="cl">3. LoadBalancer   {regionName, vpcName}  (the step everyone misses)
</span></span><span class="line"><span class="cl">4. ...and only THEN the Supervisor Namespace
</span></span></code></pre></div><p>Encode it in whatever provisions your VPCs: a script, a pipeline, an
operator. It&rsquo;s four API calls, which is a good deal cheaper than an
afternoon. And it turns a silent, undiagnosable <code>&lt;pending&gt;</code> into a platform
that just works.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>Nobody buys a platform for its ordering rules. But this is exactly the kind
of edge that decides whether self-service provisioning feels reliable or
flaky to the people using it.</p>
<p>In a customer deployment, the answer isn&rsquo;t a blog post. It&rsquo;s provisioning
automation that already does the four steps in the right order, every time,
so a tenant never sees a VIP stuck at <code>&lt;pending&gt;</code>. Knowing where the sharp
edges are, because you&rsquo;ve been cut by them in a lab, is most of what an
experienced delivery partner is for.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>In a self-service NSX VPC, <strong>the LBService must predate the namespace</strong>.
NCP discovers load balancers when the namespace is added, and never again.</li>
<li><code>FailedRealizeNSXResource: Generic error</code> on a Service means: go and read
the NCP logs. The real message (<code>Lbs &lt;ns&gt; is not found</code>, NCP00270) is
there.</li>
<li><code>VPCAttachment</code> (service gateway) is the prerequisite for the load
balancer itself. That one, at least, fails loudly.</li>
<li>Retro-tagging NSX objects to look &ldquo;owned&rdquo; doesn&rsquo;t help a cache that never
re-reads. Recreating the namespace is the only tenant-level fix.</li>
<li>While you&rsquo;re at it: new namespaces also reject VM creation until image
<code>status.disks</code> syncs (~1–3 minutes after content library attach). Build
the wait into your automation and both sharp edges disappear.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/vsphere-supervisor-installation-and-configuration/supervisor-networking-with-virtual-private-clouds.html">Deploying Supervisor with VCF Networking with VPC</a>: what NCP creates for a namespace given no VPC: a VPC with its load balancer and SNAT IP.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/vsphere-supervisor-installation-and-configuration/configuring-and-managing-vsphere-namespaces/managing-vsphere-namespaces-on-a-supervisor-with-nsx-vpc/create-and-configure-a-vsphere-namespace-on-a-supervisor-with-vpc/create-namespaces-with-vpc-nosnat-nolb.html">Create vSphere Namespaces on VPCs without SNAT and Load Balancer</a>: without the VPC&rsquo;s load balancer, LoadBalancer services and VirtualMachineServices cannot be deployed.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-vpc-connectivity-profile.html">Add a VPC Connectivity Profile</a>: the transit gateway, the service gateway and default outbound NAT.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/vsphere-supervisor-installation-and-configuration/configuring-and-managing-vsphere-namespaces/managing-vsphere-namespaces-on-a-supervisor-with-nsx-vpc/create-and-configure-a-vsphere-namespace-on-a-supervisor-with-vpc.html">Create and Configure a vSphere Namespace on a Supervisor with NSX VPC</a>: placing a new namespace in an existing VPC.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/adding-and-managing-virtual-private-clouds/add-a-vpc.html">Create a Virtual Private Cloud in VCF Automation</a>: a tenant VPC, its connectivity profile and its load balancing setting.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/sample-blueprints-in-vcf-automation-for-all-apps.html">Sample Blueprints in VCF Automation</a>: a VPC, its VPCAttachment and a namespace that depends on them, as blueprint resources.</li>
</ul>
<p><em>Previously in this series: <a href="/posts/nested-esxi-nsx-vpc/">nested ESXi inside an NSX VPC</a>.
Next: three datacenters, one IP plan, with identical isolated pods.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Output captured live, trimmed for length,
never edited for outcome.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Three datacenters, one IP plan: identical isolated pods with NSX VPCs</title>
      <link>https://thenestedlab.com/posts/three-datacenters-one-ip-plan/</link>
      <pubDate>Wed, 16 Sep 2026 08:00:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/three-datacenters-one-ip-plan/</guid>
      <description>Three nested-ESXi pods with byte-identical addressing, down to the MACs, and no route between them. How overlapping VPC address ranges turn cookie-cutter environments into a feature.</description>
      <content:encoded><![CDATA[<p>Here are three hosts, all answering to <code>vmk0 = 172.30.0.40</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ssh root@192.168.144.30  -&gt;  [root@esx01-a:~]  vmk0  172.30.0.40
</span></span><span class="line"><span class="cl">ssh root@192.168.144.32  -&gt;  [root@esx01-b:~]  vmk0  172.30.0.40
</span></span><span class="line"><span class="cl">ssh root@192.168.144.34  -&gt;  [root@esx01-c:~]  vmk0  172.30.0.40
</span></span></code></pre></div><p>Same IP. Same VLAN. Same gateway. Same <em>MAC address</em>, as it turns out. And
none of them can reach any of the others. This is the post where NSX VPCs
stop being a workaround for nested labs, and become genuinely better than
the physical alternative.</p>
<p><img alt="Three pods, identical IP plans, no route between them" loading="lazy" src="/images/product-01-hook.jpg"></p>
<h2 id="why-identical-addressing-matters">Why identical addressing matters</h2>
<p>If you&rsquo;ve ever built training pods, cert-study labs or per-team reproduction
environments, you know the pain. Every copy needs a unique address plan. So
every runbook, every screenshot and every &ldquo;type this exact command&rdquo; has to be
parameterised per pod. Students in seat 7 see different numbers from the
slides. Reproductions drift from the original.</p>
<p>The fix is obvious and normally impossible: <strong>give every pod the same
addresses</strong>. On a physical fabric, that means VRFs, per-pod NAT and a great
many favours from the network team. In an NSX VPC, it&rsquo;s the default
behaviour.</p>
<h2 id="the-mechanism-overlapping-privateips">The mechanism: overlapping privateIPs</h2>
<p>Each pod gets its own VPC, and every VPC declares the same private range:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">vpc.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VPC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">nested-vpc-a}     </span><span class="w"> </span><span class="c"># then -b, then -c</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">privateIPs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;172.30.0.0/16&#34;</span><span class="p">]</span><span class="w">      </span><span class="c"># identical in all three</span><span class="w">
</span></span></span></code></pre></div><p>A <code>Private</code> subnet is never advertised beyond its VPC, so NSX has no
objection to three VPCs carving up the same /16. The pods aren&rsquo;t &ldquo;firewalled
from each other&rdquo;. There is simply no route between them: isolation by
construction, not by policy.</p>
<p><img alt="NSX: four VPCs, four sn-mgmt subnets, same CIDR" loading="lazy" src="/images/ui/u11a-nsx-snmgmt-four-vpcs.jpg">
<em>NSX&rsquo;s subnet view filtered to <code>sn-mgmt</code>: four rows, four VPCs, one CIDR.</em></p>
<h2 id="the-trick-deterministic-realization">The trick: deterministic realization</h2>
<p>Identical <em>ranges</em> aren&rsquo;t enough. I want identical <em>subnets</em>, so that the
management gateway is <code>.33</code> and the hosts are <code>.40</code>/<code>.41</code> in every pod.</p>
<p>NSX allocates subnets from <code>privateIPs</code> in creation order, and a fresh VPC
allocates deterministically. So the topology is applied in a <strong>fixed order</strong>
(trunk, mgmt, vMotion, vSAN), and every pod realizes the same map:</p>
<table>
	<thead>
			<tr>
					<th>Subnet</th>
					<th>Realized</th>
					<th>VLAN</th>
					<th>Hosts</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>sn-trunk</td>
					<td>172.30.0.0/27</td>
					<td>—</td>
					<td>(carries the tags)</td>
			</tr>
			<tr>
					<td>sn-mgmt</td>
					<td>172.30.0.32/27</td>
					<td>1610</td>
					<td>.40 / .41, gw .33</td>
			</tr>
			<tr>
					<td>sn-vmotion</td>
					<td>172.30.0.64/27</td>
					<td>1611</td>
					<td>.70 / .71, gw .65</td>
			</tr>
			<tr>
					<td>sn-vsan</td>
					<td>172.30.0.96/27</td>
					<td>1612</td>
					<td>.100 / .101, gw .97</td>
			</tr>
	</tbody>
</table>
<p>In the catalog blueprint, <code>dependsOn</code> between the subnet resources enforces
that order. It&rsquo;s the one place a declarative tool needs to be told about
sequence. Skip it and two pods can come out with mgmt and vMotion swapped,
which works perfectly and confuses everyone.</p>
<p><img alt="NSX: nested-vpc-a expanded, the /16 private block" loading="lazy" src="/images/ui/u11-nsx-vpc-a-cidr.jpg"></p>
<h2 id="the-door-one-vip-per-host">The door: one VIP per host</h2>
<p>Each pod is unreachable from outside by design. So each host gets a
<code>VirtualMachineService</code> of type <code>LoadBalancer</code>, publishing SSH and HTTPS. The
VIPs come from the org&rsquo;s <em>external</em> block, and they&rsquo;re the only addresses
that differ between pods:</p>
<table>
	<thead>
			<tr>
					<th>Pod</th>
					<th>VPC</th>
					<th>esx01 VIP</th>
					<th>esx02 VIP</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>a</td>
					<td>nested-vpc-a</td>
					<td>192.168.144.30</td>
					<td>.31</td>
			</tr>
			<tr>
					<td>b</td>
					<td>nested-vpc-b</td>
					<td>192.168.144.32</td>
					<td>.33</td>
			</tr>
			<tr>
					<td>c</td>
					<td>nested-vpc-c</td>
					<td>192.168.144.34</td>
					<td>.35</td>
			</tr>
	</tbody>
</table>
<p>That&rsquo;s how the opening transcript works: three VIPs, three hosts, one inside
address.</p>
<h2 id="proving-the-isolation">Proving the isolation</h2>
<p>Claims are cheap. Here&rsquo;s the test matrix, from a VM in a <em>fourth</em> VPC (the
org default):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ping 172.30.0.140 (own VPC)....... REACHABLE
</span></span><span class="line"><span class="cl">ping 172.30.0.40  (pod space)..... unreachable
</span></span><span class="line"><span class="cl">curl http://172.31.0.2/ (shared).. shared-svc repo01
</span></span></code></pre></div><p><img alt="Isolation matrix: own-VPC reachable, pod space unreachable, shared service reachable" loading="lazy" src="/images/demo-c7-isolation.jpg"></p>
<p>Its own VPC&rsquo;s <code>172.30.0.140</code>: reachable. Then <code>172.30.0.40</code>, an address that
exists in three other VPCs at once: unreachable, because from here there is
no such route. (The third line is the shared-services VPC, which is <a href="/series/the-vpc-pod-papers/">the next
post</a>.)</p>
<p>Inside each pod, east-west traffic is normal: <code>esx01 → esx02</code> vmkping passes
on all three VLANs, in all three pods. And here&rsquo;s the detail I didn&rsquo;t expect.
The nested-ESXi appliance derives vmk0&rsquo;s MAC deterministically from its
config, so <strong>the three hosts share a MAC as well as an IP</strong>.</p>
<p>That&rsquo;s harmless, because each VPC is its own L2 domain. On one shared
physical segment, it would make for a rather more exciting afternoon. Here,
it&rsquo;s just a nice demonstration of how complete the separation is.</p>
<h2 id="what-this-replaces">What this replaces</h2>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Physical / VLAN-based pods</th>
					<th>VPC pods</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Identical addressing</td>
					<td>VRF per pod + NAT, fabric change per pod</td>
					<td>default behaviour</td>
			</tr>
			<tr>
					<td>Adding a pod</td>
					<td>switch config, IPAM, firewall rules</td>
					<td>one API call for the VPC, one blueprint request</td>
			</tr>
			<tr>
					<td>Isolation guarantee</td>
					<td>policy (auditable, breakable)</td>
					<td>topology (no route exists)</td>
			</tr>
			<tr>
					<td>Tenant self-service</td>
					<td>no</td>
					<td>yes — the VPC is a tenant object</td>
			</tr>
	</tbody>
</table>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>&ldquo;Identical environments&rdquo; sounds like a lab nicety. It&rsquo;s actually one of the
most requested things in enterprise IT, usually asked for in other words:</p>
<ul>
<li><strong>Training at scale</strong>: every seat in the room sees the same addresses as
the slides, so material is written once and never parameterised per pod.</li>
<li><strong>Per-engineer or per-team replicas</strong> of a reference environment, for
development and testing that behaves exactly like the original.</li>
<li><strong>Regulatory or business-unit separation</strong> on shared infrastructure,
without VRF sprawl or a bespoke firewall estate. Isolation is a property of
the topology, which is the easiest kind to evidence to an auditor.</li>
<li><strong>Blue/green copies</strong> of an environment for change rehearsal, then
cut-over or discard.</li>
</ul>
<p>On a physical network, each of these is a project. On VCF with NSX VPCs, it&rsquo;s
a template.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Overlapping <code>privateIPs</code> across VPCs is <strong>supported and intentional</strong>.
Identical pods are a feature, not a hack.</li>
<li>Fresh VPCs realize subnets <strong>deterministically in creation order</strong>. Fix
the order (<code>dependsOn</code> in a blueprint) and every pod gets the same map.</li>
<li>Pods are unreachable from outside by construction. Publish exactly what
you mean to through <code>LoadBalancer</code> VIPs from the external block.</li>
<li>Prove isolation from a <em>different</em> VPC, with a positive control (own VPC
reachable) beside the negative.</li>
<li>Expect duplicate MACs across pods from appliance images. It looks
alarming, and it&rsquo;s fine.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/create-a-vpc.html">Create a VPC</a>: private CIDRs are local to a VPC and can overlap between VPCs.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-subnet-for-the-vpc.html">Add a Subnet to a VPC</a>: Private subnets, and subnet CIDRs auto-allocated from the VPC&rsquo;s IP blocks by size.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview.html">Virtual Private Clouds Overview</a>: the access modes, NAT, and external IPs as a way in to private workloads.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/bindings-and-dependencies.html">Creating bindings and dependencies between resources in blueprints in VCF Automation</a>: <code>dependsOn</code> and the build order it sets.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/adding-and-managing-virtual-private-clouds/add-a-vpc.html">Create a Virtual Private Cloud in VCF Automation</a>: a VPC as an organization&rsquo;s own object, with its own private CIDRs.</li>
</ul>
<p><em>Previously: <a href="/posts/the-lb-that-must-exist-first/">the LB that must exist first</a>.
Next: one WSUS for pods that can&rsquo;t see each other.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Output captured live, trimmed for length,
never edited for outcome.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Shared services for isolated tenants: PrivateTGW subnets</title>
      <link>https://thenestedlab.com/posts/shared-services-for-isolated-tenants/</link>
      <pubDate>Wed, 16 Sep 2026 07:50:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/shared-services-for-isolated-tenants/</guid>
      <description>Three pods with identical addressing all need the same repo, WSUS and AD. One shared-services VPC serves them all through the transit gateway, and can&amp;rsquo;t reach back into any of them.</description>
      <content:encoded><![CDATA[<p>The pods from <a href="/posts/three-datacenters-one-ip-plan/">the last post</a> are
perfectly isolated. That&rsquo;s the requirement, and straight away the problem.
Every one of them needs Windows updates, a package repo, DNS, maybe a domain
controller. Do I really run a WSUS (Windows Server Update Services)
<em>per pod</em>?</p>
<p>No. There&rsquo;s a third subnet access mode for exactly this. The design it
enables is hub-and-spoke, with a very specific property: <strong>spokes reach the
hub; the hub cannot reach the spokes; spokes never reach each other.</strong></p>
<h2 id="the-three-access-modes">The three access modes</h2>
<p>Everything in this series comes down to one field on a VPC subnet, which is
either elegant or slightly deflating, depending on the day:</p>
<table>
	<thead>
			<tr>
					<th><code>accessMode</code></th>
					<th>Advertised to</th>
					<th>Use</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>Private</code></td>
					<td>nobody outside the VPC</td>
					<td>workloads — isolation <em>and</em> overlapping CIDRs</td>
			</tr>
			<tr>
					<td><code>PrivateTGW</code></td>
					<td>every VPC attached to the org&rsquo;s transit gateway</td>
					<td>shared services</td>
			</tr>
			<tr>
					<td><code>Public</code></td>
					<td>the external network</td>
					<td>internet/corp-facing endpoints</td>
			</tr>
	</tbody>
</table>
<p><code>PrivateTGW</code> (private transit gateway) subnets draw their addresses from a
<strong>separate transit block</strong> (here <code>172.31.0.0/…</code>), not from the VPC&rsquo;s own
<code>privateIPs</code>. That&rsquo;s the key. The shared range can&rsquo;t collide with the pods'
<code>172.30.0.0/16</code>, because it comes from a different pool that all VPCs agree
on.</p>
<h2 id="the-build">The build</h2>
<p>One more VPC, <code>shared-svc</code>, follows the same ordering rules as any other:
VPC, then VPCAttachment, then LoadBalancer, then namespace. Then a single
subnet:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">crd.nsx.vmware.com/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Subnet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sn-services}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w"> </span>{<span class="nt">accessMode</span><span class="p">:</span><span class="w"> </span><span class="nt">PrivateTGW, ipv4SubnetSize</span><span class="p">:</span><span class="w"> </span><span class="m">32</span>}<span class="w">
</span></span></span></code></pre></div><p>It realized as <code>172.31.0.0/27</code>. A VM on it, <code>svc-repo01</code> at <code>172.31.0.2</code>,
serving HTTP, became the shared repo. Not the most glamorous job in the lab,
but somebody has to do it.</p>
<p><img alt="Three pod VPCs with identical 172.30.0.0/16 Private subnets reach the shared-svc VPC&rsquo;s repo at 172.31.0.2 through the transit gateway, each SNATed to its own address; pods can&rsquo;t reach each other, and the repo can&rsquo;t route back to a pod" loading="lazy" src="/images/diagrams/shared-services-hub.svg">
<em>One way by construction: the pods reach the hub, and nothing reaches a pod.</em></p>
<h2 id="the-test-that-matters-is-directional">The test that matters is directional</h2>
<p>Reachability <em>to</em> the service is the easy claim. From <code>esx01</code> in each of the
three pods (ESXi ships python3, so <code>urllib</code> is the test client):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">pod-a esx01 -&gt; http://172.31.0.2/   200  shared-svc repo01
</span></span><span class="line"><span class="cl">pod-b esx01 -&gt; http://172.31.0.2/   200  shared-svc repo01
</span></span><span class="line"><span class="cl">pod-c esx01 -&gt; http://172.31.0.2/   200  shared-svc repo01
</span></span><span class="line"><span class="cl">default-vpc  -&gt; http://172.31.0.2/   200  shared-svc repo01
</span></span></code></pre></div><p>Three pods with <strong>identical source addresses</strong> (<code>172.30.0.40</code>) all hit one
service, and all get answers. So how does each reply find its way back to the
right pod, when three of them claim <code>.40</code>?</p>
<p>Because pod traffic crosses the transit gateway <strong>SNAT&rsquo;d to a per-VPC
address</strong> from the external IP block, by the
<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-vpc-connectivity-profile.html">default outbound NAT</a>
in each VPC&rsquo;s connectivity profile. The service never sees <code>172.30.0.40</code>; it
sees three distinct SNAT addresses. Ambiguity never arises.</p>
<p>Now the other direction, from <code>svc-repo01</code> back towards a pod:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">svc-repo01 -&gt; 172.30.0.40   unreachable
</span></span><span class="line"><span class="cl">svc-repo01 -&gt; 172.30.0.41   unreachable
</span></span></code></pre></div><p>Not &ldquo;blocked&rdquo;: <em>unroutable</em>. Pod subnets are <code>Private</code>, so they were never
advertised to the transit gateway. Even if they had been, <code>172.30.0.40</code>
would be ambiguous across three VPCs. The hub literally cannot start a
conversation with a spoke.</p>
<p>For a shared service that will one day be compromised, that&rsquo;s the property
you want. Gloomy, I know, but gloom is a perfectly good security posture.</p>
<h2 id="what-goes-in-the-hub">What goes in the hub</h2>
<p>Anything that pods <em>consume</em> and that is <em>stateless about which pod is
asking</em>: WSUS and patch mirrors, OS and package repos, container registries,
NTP, DNS forwarders, licence servers. Domain controllers work too, with the
usual caveat: identical hostnames across pods need per-pod domains or a
naming scheme.</p>
<p>What does <strong>not</strong> go in the hub: anything that needs to <em>reach into</em> a pod,
such as monitoring pollers, backup agents that pull, and jump hosts. Those
either live in the pod, or the pod publishes a <code>LoadBalancer</code> VIP for them:
the deliberate door from <a href="/posts/whats-a-vpc-with-pacman/">part 0</a>.</p>
<h2 id="tightening-further">Tightening further</h2>
<p>The transit gateway gives you reachability; policy gives you precision. A
<code>VPCGatewayFirewallPolicy</code> on <code>shared-svc</code> can restrict inbound traffic to
<code>tcp/80,443</code> from the transit range and nothing else. Then the repo is a
repo, and not a foothold.</p>
<p>I left it open for the test. You shouldn&rsquo;t: do as I say, not as I did.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>This is the pattern that makes isolated tenants <em>affordable</em>. Without it,
every isolated environment needs its own patch server, repository, DNS and
directory. That&rsquo;s cost and drift, and they quietly kill the idea.</p>
<p>With it, a customer runs one set of shared services for dozens of tenants,
and keeps them patched in one place. They can still show a security reviewer
that the shared service has no path back into any tenant.</p>
<p>The same hub serves well beyond patching: central logging and monitoring
collectors, licence servers, artifact registries, build agents. Anything
tenants consume, but shouldn&rsquo;t be reachable <em>by</em>.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li><code>PrivateTGW</code> is the shared-services mode: addresses from the <strong>transit
block</strong>, advertised to every attached VPC, no collision with pod space.</li>
<li>Access is <strong>one-way by construction</strong>: pods to service works (SNAT&rsquo;d per
VPC), and service to pod has no route. Test both directions and write down
both results.</li>
<li>Identical pod addressing and shared services coexist <em>because</em> of the
SNAT. The hub sees per-VPC SNAT addresses, never the overlapping private
ones.</li>
<li>The same ordering rules apply to the hub VPC: VPC, attachment, load
balancer, namespace, subnets, wait for image sync, then workloads.</li>
<li>Add a gateway firewall policy on the hub. Reachability is not
authorisation.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-subnet-for-the-vpc.html">Add a Subnet to a VPC</a>: the Private Transit Gateway mode: addresses from the private transit gateway blocks, reachable from the project&rsquo;s VPCs.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-vpc-connectivity-profile.html">Add a VPC Connectivity Profile</a>: private transit gateway IP blocks, and the SNAT rule default outbound NAT gives each VPC.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/transit-gateways.html">Transit Gateways</a>: traffic between a tenant&rsquo;s VPCs, and out to the external network.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview.html">Virtual Private Clouds Overview</a>: Private, Private - Transit Gateway and Public subnets side by side.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-security-load-balancing/vdefend/vdefend-firewall/9-0/secure-vpc-projects/firewall-policies-in-an-nsx-project/add-gfw-rules-for-vpc-projects.html">Add GFW Rules for VPCs</a>: north-south gateway firewall rules for a VPC&rsquo;s ingress and egress traffic.</li>
</ul>
<p><em>Previously: <a href="/posts/three-datacenters-one-ip-plan/">three datacenters, one IP plan</a>.
Next: the whole pod as a single catalog item.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Output captured live, trimmed for length,
never edited for outcome.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>A datacenter in a catalog tile: nested ESXi pods via VCF Automation All Apps</title>
      <link>https://thenestedlab.com/posts/nested-esxi-via-vcfa-all-apps/</link>
      <pubDate>Wed, 16 Sep 2026 07:40:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/nested-esxi-via-vcfa-all-apps/</guid>
      <description>The whole isolated pod, nested ESXi hosts and all, as one VCF Automation blueprint in the catalog. How the blueprint is built, the order it enforces, and the three things it can&amp;rsquo;t express.</description>
      <content:encoded><![CDATA[<p>Everything in this series so far was built with <code>kubectl</code> and API calls.
That proves the platform. It doesn&rsquo;t make a <em>product</em>.</p>
<p>This post turns the pod into a <strong>catalog item</strong>. Fill in a name, pick a VPC,
click Request, and a few minutes later there&rsquo;s a datacenter in miniature with
two SSH prompts waiting. Barely time to put the kettle on.</p>
<p><img alt="VCFA catalog: the nested-esxi-pod tile" loading="lazy" src="/images/ui/u1-catalog-tile.jpg"></p>
<h2 id="all-apps-in-one-paragraph">All Apps in one paragraph</h2>
<p>VCF Automation 9.1 has two provisioning models side by side. <strong>VM Apps</strong> is
the classic Aria Automation path: cloud templates run through an IaaS
(infrastructure as a service) engine that drives vCenter. <strong>All Apps</strong> is the
supervisor-native path. Its blueprint composes Kubernetes objects (a
Supervisor Namespace, VM Service VMs, NSX subnets, vSphere Kubernetes Service
clusters), and the vSphere Supervisor&rsquo;s controllers reconcile them.</p>
<p>A blueprint is <code>formatVersion: 2</code>, and its resources are
<code>CCI.Supervisor.Namespace</code> and <code>CCI.Supervisor.Resource</code>. The second is
literally &ldquo;here&rsquo;s a manifest, apply it in that namespace.&rdquo; So the blueprint
is a <em>composition</em> of the manifests from the earlier posts, with two
additions: inputs, and <code>dependsOn</code>.</p>
<h2 id="the-blueprint-section-by-section">The blueprint, section by section</h2>
<h3 id="inputs--the-form">Inputs — the form</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">inputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">podName</span><span class="p">:</span><span class="w">  </span>{<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="nt">string, default</span><span class="p">:</span><span class="w"> </span><span class="nt">nested-pod, pattern</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;^[a-z0-9]([-a-z0-9]*[a-z0-9])?$&#39;</span>}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vpcName</span><span class="p">:</span><span class="w">  </span>{<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="nt">string, description</span><span class="p">:</span><span class="w"> </span><span class="l">Must exist and be Realized before deploying.}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">esxOva</span><span class="p">:</span><span class="w">   </span>{<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="nt">string, default</span><span class="p">:</span><span class="w"> </span><span class="l">vmi-61bb062ddfc506b79}  </span><span class="w"> </span><span class="c"># Nested ESXi 9.1 appliance</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">isoImage</span><span class="p">:</span><span class="w"> </span>{<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="nt">string, default</span><span class="p">:</span><span class="w"> </span><span class="l">vmi-39f562e2ae9e9c501}  </span><span class="w"> </span><span class="c"># the ISO to attach</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vmClass</span><span class="p">:</span><span class="w">  </span>{<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="nt">string, default</span><span class="p">:</span><span class="w"> </span><span class="nt">best-effort-large, enum</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">best-effort-large, best-effort-xlarge, best-effort-2xlarge]}</span><span class="w">
</span></span></span></code></pre></div><p><img alt="The request form" loading="lazy" src="/images/ui/u2-request-form.jpg"></p>
<h3 id="the-namespace--with-libraries-attached">The namespace — with libraries attached</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">namespace</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">${input.podName}-       </span><span class="w"> </span><span class="c"># NOT name — new namespaces get a suffix</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">className</span><span class="p">:</span><span class="w"> </span><span class="l">large</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">regionName</span><span class="p">:</span><span class="w"> </span><span class="l">f06</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">vpcName</span><span class="p">:</span><span class="w"> </span><span class="l">${input.vpcName}             </span><span class="w"> </span><span class="c"># pins the namespace to the pod&#39;s VPC</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">storageClasses</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">vSAN Default Storage Policy, limit</span><span class="p">:</span><span class="w"> </span><span class="l">400000Mi}]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">zones</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">domain-c9, cpuLimit</span><span class="p">:</span><span class="w"> </span><span class="nt">40000M, memoryLimit</span><span class="p">:</span><span class="w"> </span><span class="l">64000Mi, ...}]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">contentSources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">ISO, type</span><span class="p">:</span><span class="w"> </span><span class="l">ContentLibrary}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">f06-vks-lib01, type</span><span class="p">:</span><span class="w"> </span><span class="l">ContentLibrary}</span><span class="w">
</span></span></span></code></pre></div><p><code>contentSources</code> is the line that closes the gap a lot of first attempts
hit. Our libraries were plain vCenter libraries, and a namespace created by
VCF Automation got <strong>none of them</strong>. No libraries meant no
<code>VirtualMachineImage</code>s, so nothing could be deployed. An empty namespace is
very tidy, and of no use to anyone.</p>
<p>Declaring the libraries here attaches them at creation. (The 9.1 docs say a
<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-projects-in-vcfa/create-a-namespace-class.html">namespace class</a>
is assigned a content library automatically, and provider libraries are
shared with every namespace.)</p>
<h3 id="the-topology--ordered-on-purpose">The topology — ordered on purpose</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">snTrunk</span><span class="p">:</span><span class="w">   </span>{<span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="nt">CCI.Supervisor.Resource, properties</span><span class="p">:</span><span class="w"> </span>{<span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="l">${resource.namespace.id}, manifest: &lt;Subnet sn-trunk&gt;}}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">snMgmt</span><span class="p">:</span><span class="w">    </span>{<span class="nt">dependsOn</span><span class="p">:</span><span class="w"> </span><span class="nt">[snTrunk], ...  manifest</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;Subnet sn-mgmt&gt;}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">snVmotion</span><span class="p">:</span><span class="w"> </span>{<span class="nt">dependsOn</span><span class="p">:</span><span class="w"> </span><span class="nt">[snMgmt],  ...  manifest</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;Subnet sn-vmotion&gt;}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">bmMgmt</span><span class="p">:</span><span class="w">    </span>{<span class="nt">... manifest</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;SubnetConnectionBindingMap sn-mgmt -&gt; sn-trunk, vlan 1610&gt;}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">bmVmotion</span><span class="p">:</span><span class="w"> </span>{<span class="nt">... manifest</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;SubnetConnectionBindingMap sn-vmotion -&gt; sn-trunk, vlan 1611&gt;}</span><span class="w">
</span></span></span></code></pre></div><p>The <code>dependsOn</code> chain is the whole reason <a href="/posts/three-datacenters-one-ip-plan/">every pod has identical
CIDRs</a>. A fresh VPC realizes its
subnets in the order they were created, and the blueprint fixes that order.</p>
<p><img alt="Blueprint canvas and YAML side by side" loading="lazy" src="/images/ui/u3-blueprint-canvas-yaml.jpg"></p>
<h3 id="the-hosts--dual-nic-iso-attached-bootstrapped-by-ovf">The hosts — dual-NIC, ISO attached, bootstrapped by OVF</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">esx01</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">CCI.Supervisor.Resource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dependsOn</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">bmMgmt]                   </span><span class="w"> </span><span class="c"># no point booting before VLAN 1610 exists</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">manifest</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">VirtualMachine               </span><span class="w"> </span><span class="c"># vmoperator.vmware.com/v1alpha5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">hardware</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">cdrom</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">... the ISO, declared, connected ... ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">network</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">interfaces</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">eth0 -&gt; sn-trunk, eth1 -&gt; sn-trunk ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">vAppConfig</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">guestinfo.hostname / ipaddress / vlan / ... ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">wait</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">fields</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{<span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="nt">status.powerState, value</span><span class="p">:</span><span class="w"> </span><span class="l">PoweredOn}]</span><span class="w">
</span></span></span></code></pre></div><p>(Abridged: the full resource carries the image references, VM class,
guest ID and the complete <code>guestinfo</code> set.)</p>
<p>Two vNICs, both on the trunk: <a href="/series/the-vpc-pod-papers/">the nested equivalent of a VCF host&rsquo;s two
pNICs</a>. The ISO rides along as a declarative
CD-ROM.</p>
<p>The <code>wait</code> block makes the deployment&rsquo;s <em>completion</em> mean something: the
request doesn&rsquo;t finish until the host is powered on. Finishing any earlier
would be optimism, not automation.</p>
<h3 id="the-doors--one-vip-per-host">The doors — one VIP per host</h3>
<p>Each host gets a <code>VirtualMachineService</code> of type <code>LoadBalancer</code>, which
selects it by label and publishes 22 and 443. A blueprint <strong>output</strong> then
reads the VIP back out of the service&rsquo;s status:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">outputs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">esx01Ssh</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;ssh root@${resource.esx01Access.object.status.loadBalancer.ingress[0].ip}&#34;</span>}<span class="w">
</span></span></span></code></pre></div><p>The outputs show up in the deployment view, so the requester gets the SSH
command rather than a scavenger hunt.</p>
<p><img alt="Deployment topology after a successful request" loading="lazy" src="/images/ui/u4-deployment-topology.jpg"></p>
<figure class="nl-video">
  <video autoplay loop muted playsinline controls preload="metadata" style="aspect-ratio:1344 / 788" poster="/images/u7-catalog-request-flow-poster.jpg">
    <source src="/images/u7-catalog-request-flow.mp4" type="video/mp4">
  </video>
  <figcaption>The request flow, end to end: request → deployment in progress → complete.</figcaption>
</figure>

<h2 id="what-the-blueprint-cannot-express-yet">What the blueprint cannot express (yet)</h2>
<p>Three cluster-scoped objects must exist <em>before</em> the request, <a href="/posts/the-lb-that-must-exist-first/">in this order</a>:</p>
<ol>
<li><code>VPC</code>: <code>privateIPs: 172.30.0.0/16</code>, the same in every pod.</li>
<li><code>VPCAttachment</code>: the connectivity profile with the service gateway.
Without it, creating the load balancer fails loudly.</li>
<li><code>LoadBalancer</code>: silently, permanently required before the namespace.</li>
</ol>
<p>VCF Automation 9.1 does have blueprint types for the first two: <code>CCI.VPC</code>,
and <code>CCI.VPC.Configuration</code> with <code>kind: VPCAttachment</code>. One of its
<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/sample-blueprints-in-vcf-automation-for-all-apps.html">sample blueprints</a>
uses both. There is none for the third.</p>
<p>A later test confirmed it. <code>CCI.VPC.Configuration</code> rejects a <code>LoadBalancer</code>
kind, and a VPC created that way comes up with load balancing off. Its
namespace&rsquo;s VIPs would sit pending, waiting for a load balancer that isn&rsquo;t
coming.</p>
<p>Today that&rsquo;s a short script or a runbook step per pod. The honest framing:
the blueprint is the <em>pod</em>, the VPC is the <em>tenancy</em>, and tenancy is still
created one layer up. I&rsquo;d expect the load balancer to become blueprintable
too. Until then, keep the three calls next to the blueprint in version
control.</p>
<h2 id="publishing-one-version-at-a-time">Publishing: one version at a time</h2>
<p>The order is blueprint, then <code>BlueprintVersion</code>, then release. Validation
happens at <em>version</em> time, not at create time. The result lives in
<code>status.validationMessages</code> rather than the HTTP code, so a 200 with
<code>ContentValid: False</code> is a thing: the HTTP equivalent of &ldquo;yes, but no&rdquo;.</p>
<p>And only <strong>one</strong> version can be published. Unrelease 1.0.0 before releasing
1.1.0, or you get a 409. (The full list of sharp edges is
<a href="/series/the-vpc-pod-papers/">its own post</a>.)</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>This is where platform engineering turns into a service. The gap between &ldquo;we
can build you an environment&rdquo; and &ldquo;request one from the catalog&rdquo; is the gap
between days and minutes. It&rsquo;s also the gap between a bespoke build and one
that is consistent, quota-controlled and recorded every time. For an
organisation that means:</p>
<ul>
<li><strong>Time-to-environment</strong> measured in minutes, requested by the people who
need it, without a queue.</li>
<li><strong>Consistency by construction.</strong> Every environment comes from the same
definition, so support, training material and runbooks all match.</li>
<li><strong>Governance built in.</strong> Quotas, ownership, history and clean teardown are
properties of the deployment record, not a spreadsheet.</li>
</ul>
<p>The nested-ESXi pod is one catalog item. The same approach delivers any
shape of environment: application stacks for developers, sandboxes for a
proof of concept, demo kits for a sales team, isolated builds for a partner.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>All Apps blueprints are <strong>compositions of manifests</strong>: <code>CCI.Supervisor.Namespace</code>
plus <code>CCI.Supervisor.Resource</code> per object. If it works with <code>kubectl</code>, it
works in a blueprint.</li>
<li>Use <code>generateName</code>, not <code>name</code>, for the namespace, and <code>contentSources</code> to
attach libraries at creation. Keep <code>zones</code>/<code>storageClasses</code> flat, not
wrapped.</li>
<li><code>dependsOn</code> is how you get <strong>deterministic CIDRs</strong>: order the subnets.</li>
<li><code>wait.fields</code> turns &ldquo;request complete&rdquo; into &ldquo;host is powered on&rdquo;.</li>
<li>VPC, VPCAttachment and LoadBalancer are <strong>prerequisites outside the
blueprint</strong>, in that order, before every request.</li>
<li>One published version per blueprint. Validation results live in <code>status</code>,
not in the HTTP response.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/sample-blueprints-in-vcf-automation-for-all-apps.html">Sample Blueprints in VCF Automation</a>: <code>CCI.Supervisor.Namespace</code> and <code>CCI.Supervisor.Resource</code> examples, with <code>generateName</code> and <code>context</code>.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/bindings-and-dependencies.html">Creating bindings and dependencies between resources in blueprints in VCF Automation</a>: <code>dependsOn</code> and property bindings, which set the build order.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/specifying-formatversion-in-your-blueprints.html">Specifying formatVersion in Blueprints in VCF Automation</a>: what <code>formatVersion: 2</code> adds, outputs included.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/managing-blueprints-in-vcf-automation/blueprint-versioning.html">Versioning Blueprints in VCF Automation</a>: blueprint versions, and releasing one to the catalog.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-service-administration-and-development/9-1/provision-and-manage-virtual-machines/deploying-and-managing-virtual-machines-in-vsphere-iaas-control-plane/creating-and-managing-content-libraries-for-stand-alone-vms-in-iaas-platform.html">Creating and Managing Content Libraries for Stand-Alone VMs in vSphere Supervisor</a>: VM content libraries and the namespaces they are associated with.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/vm-service/deploy-vms-with-configurable-ovf-properties-vsphere-iaas-control-plane.html">Deploy VMs with Configurable OVF Properties in vSphere Supervisor</a>: OVF properties set through the VM Service&rsquo;s vAppConfig transport.</li>
</ul>
<p><em>Previously: <a href="/posts/shared-services-for-isolated-tenants/">shared services for isolated tenants</a>.
This closes the Pod Papers&rsquo; core arc. The companion posts on
<a href="/series/the-vpc-pod-papers/">dual-NIC</a>, <a href="/series/the-vpc-pod-papers/">no-DHCP bootstrap</a>
and <a href="/series/the-vpc-pod-papers/">blueprint gotchas</a> fill in the details.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Blueprint <code>nested-esxi-pod</code> 1.1.0 is
live in the lab catalog; YAML above trimmed for length.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Dual-NIC nested hosts: what redundancy means when the fabric is virtual</title>
      <link>https://thenestedlab.com/posts/dual-nic-nested-hosts/</link>
      <pubDate>Wed, 16 Sep 2026 07:30:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/dual-nic-nested-hosts/</guid>
      <description>A second vNIC on a nested host adds no physical redundancy, so why add it? We expected bringup to want one, and pulling vmnic0 mid-SSH proved the trunk copes: 0% loss, session intact.</description>
      <content:encoded><![CDATA[<p>&ldquo;Naturally, a VCF host has at least two NICs. Are we testing that, or have
you virtualised it away?&rdquo;</p>
<p>Fair question, and like most fair questions it has an irritating two-part answer. In a nested lab, the
outer host provides the <em>physical</em> redundancy: its vDS and its NSX uplinks.
A second vNIC on the nested VM adds exactly none of that.</p>
<p>But VCF doesn&rsquo;t know it&rsquo;s nested. We expected bringup&rsquo;s host validation, and
the vDS uplink teaming it sets up, to <strong>want two vmnics</strong>. We never tried a
host with one. For the record, Broadcom&rsquo;s docs
<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/building-your-private-cloud-infrastructure/host-management/commission-hosts.html">allow single-pNIC hosts</a>,
and the VCF Installer&rsquo;s
<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">9.1 known issue</a>
only bites single-pNIC hosts that use an NFS datastore.</p>
<p>So our nested hosts get two vNICs, both on the trunk subnet. Which leaves the
fun question (for a given value of fun): does failover between them actually
work inside a VPC?</p>
<p><img alt="The failover test end to end: an SSH session through the NSX load balancer into the trunk subnet and esx01&rsquo;s two vNICs; vmnic0 fails and every VLAN moves to vmnic1" loading="lazy" src="/images/diagrams/dual-nic-failover.svg">
<em>The whole test on one page. The session I&rsquo;m typing in rides the very path I break.</em></p>
<h2 id="the-setup">The setup</h2>
<p>Both vNICs sit on the same <code>sn-trunk</code> subnet
(<a href="/posts/nested-esxi-nsx-vpc/">the trunk from part 1</a>), and ESXi sees two
10G vmnics:</p>
<p><img alt="Host Client, Physical Adapters: vmnic0 and vmnic1, both 10 Gbit/s, both on vSwitch0" loading="lazy" src="/images/ui/u13-hostclient-dual-nics-marked.jpg"></p>
<p>vSwitch0 teams them active/active with the default originating-port-ID
policy. Every port group inherits it: Management 1610, vMotion 1611 and
vSAN 1612. Nothing you wouldn&rsquo;t do on metal. Thrilling stuff, I know.</p>
<h2 id="the-test-pull-a-nic-while-watching-from-inside">The test: pull a NIC while watching from inside</h2>
<p>The interesting part isn&rsquo;t whether pings carry on. It&rsquo;s <em>which</em> session I&rsquo;m
watching from.</p>
<p>I&rsquo;m SSH&rsquo;d into <code>esx01</code> <strong>through its public VIP</strong>. So my session runs through
the NSX load balancer, the VPC, the trunk port and whichever vmnic happens to
carry vmk0. If failover breaks anything, it breaks the terminal I&rsquo;m typing
in: the networking equivalent of sawing off the branch you&rsquo;re sitting on.
Low effort, high stakes: the best kind of test.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[root@esx01-a:~] esxcli network nic list
</span></span><span class="line"><span class="cl">Name    ...  Admin Status  Link Status  Speed  MAC Address
</span></span><span class="line"><span class="cl">vmnic0  ...  Up            Up           10000  04:50:56:00:5c:03
</span></span><span class="line"><span class="cl">vmnic1  ...  Up            Up           10000  04:50:56:00:68:00
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[root@esx01-a:~] esxcli network nic down -n vmnic0    # FAIL THE FIRST NIC
</span></span><span class="line"><span class="cl">vmnic0  ...  Down          Down             0  04:50:56:00:5c:03
</span></span><span class="line"><span class="cl">vmnic1  ...  Up            Up           10000  04:50:56:00:68:00
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[root@esx01-a:~] vmkping -I vmk1 172.30.0.71          # vMotion VLAN, now over vmnic1
</span></span><span class="line"><span class="cl">3 packets transmitted, 3 packets received, 0% packet loss
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[root@esx01-a:~] vmkping -I vmk2 172.30.0.101         # vSAN VLAN, now over vmnic1
</span></span><span class="line"><span class="cl">3 packets transmitted, 3 packets received, 0% packet loss
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[root@esx01-a:~] esxcli network nic up -n vmnic0      # restore
</span></span><span class="line"><span class="cl"># session never dropped.
</span></span></code></pre></div><figure class="nl-video">
  <video autoplay loop muted playsinline controls preload="metadata" style="aspect-ratio:1568 / 604" poster="/images/c6-nic-failover-beforeafter-poster.jpg">
    <source src="/images/c6-nic-failover-beforeafter.mp4" type="video/mp4">
  </video>
  <figcaption>Before and after: vmnic0 down, every VLAN still passing.</figcaption>
</figure>

<details class="nl-fold">
<summary>The full transcript, as captured</summary>
<p><img alt="Full failover transcript" loading="lazy" src="/images/demo-c6-nic-failover.jpg"></p>

</details>

<p>Every VLAN moved to vmnic1. Zero loss on vMotion and vSAN. And the
management session, the one <em>most</em> likely to notice, never blinked. Slightly
disappointing, if I&rsquo;m honest; I&rsquo;d prepared a dramatic paragraph.</p>
<h2 id="why-this-is-a-real-result-not-a-party-trick">Why this is a real result, not a party trick</h2>
<p>Think about what just happened at the NSX layer. vmk0 has a MAC of its own,
one ESXi made up rather than the vNIC&rsquo;s. NSX had learned it on trunk port A.
When vmnic0 went down, the same MAC turned up on trunk port B, mid-flow, with
a live TCP session riding on it.</p>
<p>On a <strong>standard</strong> VPC subnet port, that is exactly what SpoofGuard exists to
stop. The port&rsquo;s address bindings pin one MAC. A frame from a different MAC,
or the <em>same</em> MAC arriving on a different port, gets dropped. Part 1 showed
that rule silencing a host on a standard subnet before it ever spoke.</p>
<p>This test shows the trunk subnet calmly accepting a foreign MAC that moves
between two of its ports. Nested vSphere needs exactly that property, and so
does anything else with a vSwitch inside a VM.</p>
<p>So the second vNIC buys three things, none of them physical redundancy:</p>
<ol>
<li><strong>Bringup and vDS teaming get the two uplinks</strong> we expected them to want.</li>
<li><strong>The teaming policy you&rsquo;ll configure in production gets exercised:</strong>
uplink failover, active/standby for vSAN, whatever you&rsquo;re rehearsing.</li>
<li><strong>A live proof that the trunk carries MAC mobility.</strong> That&rsquo;s the real
reassurance: the design isn&rsquo;t relying on a quiet network.</li>
</ol>
<p>One caution if the host is heading into a VCF Installer bringup. The 9.x
installer&rsquo;s validation wants exactly one physical NIC on vSwitch0, and stops
with &ldquo;has 2 Physical NICs connected to vSphere Standard Switch vSwitch0
(Expecting 1)&rdquo; (<a href="https://knowledge.broadcom.com/external/article/415469">KB 415469</a>).
Give such a host its second vNIC, but leave vmnic1 unclaimed until bringup
takes it. The test above teams both on vSwitch0 because it tests the trunk,
not a bringup.</p>
<h2 id="what-it-does-not-buy-and-how-to-say-so">What it does <em>not</em> buy, and how to say so</h2>
<p>If someone asks &ldquo;is this host redundant?&rdquo;, the honest answer is &ldquo;the nested
host believes it is, bless it; the actual redundancy lives one layer down.&rdquo;</p>
<p>In a training pod, that&rsquo;s the right answer. Students configure and test
failover exactly as they would on metal, while the outer platform does the
real work. It&rsquo;s usually enough to reproduce a NIC-teaming issue from the
field too: most teaming bugs live in ESXi&rsquo;s policy handling, not in the copper.</p>
<p>Where it genuinely falls short: anything about physical link behaviour. LACP
negotiation, LLDP, a flapping link, an MTU mismatch on one uplink. The virtual
fabric never fails lopsidedly, so it can&rsquo;t reproduce any of those.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>The real value is knowing <em>what a nested environment can and can&rsquo;t prove</em>,
so you can tell when a virtual lab is enough. For training, upgrade
rehearsals, configuration and policy testing, and most &ldquo;how does it behave
when…&rdquo; questions, nested is enough and far cheaper. For physical link
behaviour (LACP, optics, lopsided faults) you still want metal. Making that
call with confidence is worth more than the test itself.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Give nested VCF hosts <strong>two vNICs on the same trunk subnet</strong>. We expected
bringup and vDS teaming to want at least two vmnics, and humouring them
costs nothing. Before a VCF Installer bringup, leave vmnic1 off vSwitch0
(KB 415469).</li>
<li>Test failover <strong>from a session that depends on it</strong> (SSH through the VIP).
Pings passing while your terminal dies is not success, however much the
change ticket would like it to be.</li>
<li>The trunk subnet tolerates <strong>a vmk MAC moving between ports mid-flow</strong>.
Standard subnets lack that property, and nested vSphere needs it.</li>
<li>Be precise in the write-up: nested dual-NIC gives <em>policy</em> realism, not
<em>physical</em> redundancy. Physical link faults can&rsquo;t be reproduced here.</li>
<li>Rebuilds re-run the vmk config: the appliance creates vmk0 only, so vmk1,
vmk2, their VLANs and override gateways are applied after boot.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/9-0/vsphere-networking/networking-policies/teaming-and-failover-policy/configure-nic-teaming-and-load-balancing-on-a-standard-switch-or-port-group.html">Configure NIC Teaming, Failover, and Load Balancing on a vSphere Standard Switch or Standard Port Group</a>: the default originating-port policy, failover order, and port groups inheriting the switch&rsquo;s policy.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/segments/segment-profiles/understanding-spoofguard-segment-profile.html">Understanding SpoofGuard Segment Profile</a>: the address bindings a standard port enforces.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/segments/segment-profiles/understanding-mac-discovery-segment-profile.html">Understanding MAC Discovery Segment Profile</a>: MAC learning for nested hypervisors, with many MACs behind one vNIC.</li>
<li><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">VCF Installer</a>: the VCF 9.1 known issue where single-pNIC hosts fail NFS datastore validation, and the fix of two or more pNICs.</li>
<li><a href="https://knowledge.broadcom.com/external/article/313547/support-for-running-esxi-as-a-nested-vir.html">Support for running ESXi as a nested virtualization solution</a>: nested ESXi is not supported in production, and is encouraged for learning, training and testing.</li>
<li><a href="https://knowledge.broadcom.com/external/article/415469">VCF 9.0 Installer validation fails at ESX Host Configuration (KB 415469)</a>: one physical NIC on vSwitch0 before deployment, the other left unclaimed.</li>
</ul>
<p><em>Companion to <a href="/posts/nested-esxi-nsx-vpc/">nested ESXi inside an NSX VPC</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own. Output captured live, trimmed for length,
never edited for outcome.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Our VPC subnets have no DHCP — and that&#39;s fine</title>
      <link>https://thenestedlab.com/posts/vpc-subnets-have-no-dhcp/</link>
      <pubDate>Wed, 16 Sep 2026 07:20:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/vpc-subnets-have-no-dhcp/</guid>
      <description>The nested ESXi appliance sat at &amp;lsquo;waiting for DHCP&amp;rsquo; for ever. VPC subnets don&amp;rsquo;t hand out addresses: the VM Service does, through cloud-init, sysprep or OVF guestinfo, depending on the guest.</description>
      <content:encoded><![CDATA[<p>The second trap from <a href="/posts/nested-esxi-nsx-vpc/">part 1</a> deserves its own
short post, because it catches everything, not just ESXi: <strong>our VPC subnets
have DHCP deactivated</strong>. NSX VPC subnets can run a DHCP server or relay
(<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-subnet-for-the-vpc.html">Add a Subnet to a VPC</a>).
But the subnets the Supervisor made for us, and every <code>Subnet</code> we created
with the defaults, came up <code>DHCP_DEACTIVATED</code> with static IP allocation.</p>
<p>Drop a stock appliance onto one and it will boot, sit at &ldquo;waiting for DHCP&rdquo;,
and wait politely until the heat death of the universe.</p>
<p>This isn&rsquo;t a gap. It&rsquo;s the model. NSX allocates the address at the <em>port</em>
and pins it there with address bindings, and the <em>guest</em> has to be told what
it was given. The VM Service does that telling through <strong>bootstrap
providers</strong>. Once you know the three of them, static addressing stops being
a chore and starts being a feature.</p>
<h2 id="three-providers-three-guest-types">Three providers, three guest types</h2>
<table>
	<thead>
			<tr>
					<th>Guest</th>
					<th>Provider</th>
					<th>Carries</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Linux</td>
					<td><code>cloudInit</code></td>
					<td>user-data (users, <code>write_files</code>, <code>runcmd</code>) + network config</td>
			</tr>
			<tr>
					<td>Windows</td>
					<td><code>sysprep</code></td>
					<td>unattend XML / sysprep spec, identity, network</td>
			</tr>
			<tr>
					<td>Appliances (OVF)</td>
					<td><code>vAppConfig</code></td>
					<td>OVF properties (<code>guestinfo.*</code>) the appliance reads on boot</td>
			</tr>
	</tbody>
</table>
<p>All three are <strong>typed fields on the <code>VirtualMachine</code> object</strong>, not bolt-on
customisation specs. The platform already knows the network side: the VM
Service knows which subnet each interface landed on, and what NSX allocated.
So for cloud-init and sysprep, the addressing is injected for you.
Appliances are the exception, because each one has its own idea of which
properties it wants.</p>
<h2 id="appliances-vappconfig">Appliances: vAppConfig</h2>
<p>The nested-ESXi appliance reads <code>guestinfo.*</code> OVF properties. In the VM
Service spec, that&rsquo;s:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">vAppConfig</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.hostname,  value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;esx01.pod-a.res.lab&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.ipaddress, value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;172.30.0.40&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.netmask,   value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;255.255.255.224&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.gateway,   value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;172.30.0.33&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.vlan,      value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;1610&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.dns,       value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;10.20.52.1&#34;</span>}}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- {<span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="nt">guestinfo.ssh,       value</span><span class="p">:</span><span class="w"> </span>{<span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;True&#34;</span>}}<span class="w">
</span></span></span></code></pre></div><p>The one that trips people up: <strong>the address you give must be the one NSX
allocated to the port</strong>. In a standard subnet, SpoofGuard enforces that, and
it doesn&rsquo;t negotiate. On a trunk subnet, the VLAN subnets have their own
allocations, and you&rsquo;re choosing addresses within them.</p>
<p>Either way, pick from the realized range. And remember that <a href="/posts/nested-esxi-nsx-vpc/">recreating a VM
reallocates its addresses</a>. So the fixed
<code>.40</code>/<code>.41</code> in a <a href="/posts/three-datacenters-one-ip-plan/">deterministic pod</a>
is a design choice, not luck.</p>
<h2 id="linux-cloud-init">Linux: cloud-init</h2>
<p>A Secret holding user-data, referenced from the VM:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cloudInit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">cloudConfig</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">users</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">... a local user with a key ... ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">write_files</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/var/www/html/index.html</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;shared-svc repo01\n&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">runcmd</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="p">[</span><span class="l">systemctl, enable, --now, nginx]</span><span class="w">
</span></span></span></code></pre></div><p>Networking arrives through the platform&rsquo;s own network-config, so you don&rsquo;t
write it. Every line of YAML I don&rsquo;t write is a line I can&rsquo;t get wrong.</p>
<p>The <code>svc-repo01</code> VM from <a href="/posts/shared-services-for-isolated-tenants/">the shared-services
post</a> was exactly this:
<code>write_files</code> plus <code>runcmd</code>, with its web page verified from three pods.</p>
<h2 id="windows-sysprep">Windows: sysprep</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sysprep</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">sysprep</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">guiUnattended</span><span class="p">:</span><span class="w"> </span>{<span class="nt">autoLogon</span><span class="p">:</span><span class="w"> </span><span class="nt">true, autoLogonCount</span><span class="p">:</span><span class="w"> </span><span class="nt">1, timeZone</span><span class="p">:</span><span class="w"> </span><span class="m">85</span>}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">identification</span><span class="p">:</span><span class="w"> </span>{<span class="nt">joinWorkgroup</span><span class="p">:</span><span class="w"> </span><span class="l">WORKGROUP}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">userData</span><span class="p">:</span><span class="w"> </span>{<span class="nt">fullName</span><span class="p">:</span><span class="w"> </span><span class="nt">Lab, orgName</span><span class="p">:</span><span class="w"> </span><span class="nt">Lab, computerName</span><span class="p">:</span><span class="w"> </span>{<span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">win01}}</span><span class="w">
</span></span></span></code></pre></div><p>Or use <code>rawSysprep</code> with an unattend XML in a Secret, if you already have
one. The ISO from <a href="/posts/nested-esxi-via-vcfa-all-apps/">the blueprint post</a>
can ride along as a declarative <code>hardware.cdrom</code>. That&rsquo;s handy for tools and
agents on first boot.</p>
<h2 id="the-esxi-footnote-one-gateway-many-vmks">The ESXi footnote: one gateway, many vmks</h2>
<p>Once the appliance is up and you add vMotion and vSAN vmks on their own
subnets, you hit a detail that makes the Host Client <em>look</em> wrong:</p>
<p><img alt="Host Client: vmk0/1/2, one service each" loading="lazy" src="/images/ui/u12-hostclient-vmk-adapters.jpg"></p>
<p>ESXi&rsquo;s default TCP/IP stack has <strong>one</strong> default gateway (vmk0&rsquo;s <code>.33</code>), and
the UI repeats it on every vmk row with total confidence. Same-subnet vMotion
never uses a gateway, so nothing breaks. But each NSX subnet <em>does</em> have its
own gateway, and cross-subnet traffic from vmk1 and vmk2 would take the wrong
exit. Set per-vmk override gateways:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">esxcli network ip interface ipv4 set -i vmk1 -t static -I 172.30.0.70  -N 255.255.255.224 -g 172.30.0.65
</span></span><span class="line"><span class="cl">esxcli network ip interface ipv4 set -i vmk2 -t static -I 172.30.0.100 -N 255.255.255.224 -g 172.30.0.97
</span></span></code></pre></div><p>Now the display is truthful and the routing is correct. (The full-realism
alternative is a dedicated <code>vmotion</code> netstack for vmk1. I kept the default
stack so the service tags stay visible in the Host Client.)</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>For the business, &ldquo;no DHCP&rdquo; translates into something security and
operations teams both want: <strong>predictable addressing</strong>. Every environment has
a known address plan, firewall rules can be written once, and nothing turns
up on the network with an address nobody expected.</p>
<p>Bootstrap providers deliver the second benefit. Images stay generic and
configuration is injected at deploy time, so there are fewer golden images to
maintain and far less drift between environments.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li><strong>No DHCP in our VPC subnets</strong>: that&rsquo;s the Supervisor&rsquo;s default, not a VPC
limit. NSX allocates at the port, and the guest is told through a
bootstrap provider.</li>
<li><code>cloudInit</code> (Linux), <code>sysprep</code> (Windows) and <code>vAppConfig</code> (appliances) are
typed fields on the VM, not customisation specs.</li>
<li>Appliance addresses must match the <strong>realized</strong> subnet. Fix the order of
subnet creation if you want fixed addresses across pods.</li>
<li>ESXi has <strong>one</strong> default gateway per stack. Set <code>-g</code> per vmk, or the Host
Client lies to you and cross-subnet traffic exits wrong.</li>
<li>A static IP plan is a feature in a lab: it&rsquo;s what makes screenshots,
runbooks and pods identical.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/virtual-private-cloud-in-nsx/virtual-private-clouds-overview/add-a-subnet-for-the-vpc.html">Add a Subnet to a VPC</a>: a subnet&rsquo;s DHCP setting: none for static addresses, a DHCP server, or DHCP relay.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/advanced-network-management/segments/segment-profiles/understanding-spoofguard-segment-profile.html">Understanding SpoofGuard Segment Profile</a>: the port address bindings SpoofGuard enforces.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/vm-service/provision-a-vm-using-the-iaas-services-console-in-vcf-automation.html">Provision a VM Using Self-Service</a>: the four bootstrap methods, cloud-init, Sysprep, Linuxprep and vAppConfig, and static IP allocation.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/vm-service/deploy-vms-with-configurable-ovf-properties-vsphere-iaas-control-plane.html">Deploy VMs with Configurable OVF Properties in vSphere Supervisor</a>: OVF properties set through the VM Service&rsquo;s vAppConfig transport.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/9-0/vsphere-networking/setting-up-vmkernel-networking/configure-the-vmkernel-adapter-gateway-by-using-esxcli.html">Configure the VMkernel Adapter Gateway by Using esxcli Commands</a>: a gateway per VMkernel adapter, set with esxcli.</li>
</ul>
<p><em>Companion to <a href="/posts/nested-esxi-nsx-vpc/">nested ESXi inside an NSX VPC</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
