<?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>VKS on The Nested Lab</title>
    <link>https://thenestedlab.com/products/vks/</link>
    <description>Recent content in VKS 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/vks/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>Telegraf on VKS: the dependency that isn&#39;t in the README</title>
      <link>https://thenestedlab.com/posts/telegraf-vks-management-proxy/</link>
      <pubDate>Wed, 23 Sep 2026 06:10:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/telegraf-vks-management-proxy/</guid>
      <description>On VCF 9.1, the supported route installs Telegraf in new VKS clusters for you. Underneath sits a dependency the README never mentions, two secrets from the Supervisor Management Proxy, and without them every Telegraf pod is stuck.</description>
      <content:encoded><![CDATA[<p>The <a href="/posts/telegraf-windows-2025/">Windows half of this series</a> was about
an agent that works on an OS the vendor hasn&rsquo;t listed. This one is the
opposite: a package on a fully supported platform that does nothing,
silently, because of a dependency its own documentation doesn&rsquo;t mention.</p>
<p>The supported route comes first. The rest is the path underneath, as it
ran on f06 (VCF 9.1).</p>
<h2 id="the-symptom">The symptom</h2>
<p>Install the Telegraf package on a VKS cluster with the metric proxy flag
set (<code>isMetricProxyConfigured: true</code>, which you want when metrics go to
VCF Operations). Every Telegraf pod then stays in <code>ContainerCreating</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">NAME           PACKAGE NAME                         PACKAGE VERSION         DESCRIPTION                                                            AGE   PAUSED
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">telegraf       telegraf.kubernetes.vmware.com       1.34.4+vmware.2-vks.1   Reconcile failed: Error (see .status.usefulErrorMessage for details)   15m
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kapp: Error: waiting on reconcile deployment/telegraf (apps/v1) namespace: tanzu-system-telegraf:
</span></span><span class="line"><span class="cl">  Finished waiting unsuccessfully:
</span></span><span class="line"><span class="cl">    Deployment is not progressing:
</span></span><span class="line"><span class="cl">      ProgressDeadlineExceeded, message:
</span></span><span class="line"><span class="cl">        ReplicaSet &#34;telegraf-7cd76f96&#34; has timed out progressing.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">NAME                      READY   STATUS              RESTARTS   AGE
</span></span><span class="line"><span class="cl">telegraf-26ppl            0/1     ContainerCreating   0          15m
</span></span><span class="line"><span class="cl">telegraf-7cd76f96-wbthp   0/1     ContainerCreating   0          15m
</span></span><span class="line"><span class="cl">telegraf-g4qlm            0/1     ContainerCreating   0          15m
</span></span><span class="line"><span class="cl">telegraf-h2pcq            0/1     ContainerCreating   0          15m
</span></span></code></pre></div><p>Describe one, and the reason is <code>FailedMount</code>: two secrets don&rsquo;t exist.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">  Warning  FailedMount  5m14s (x13 over 15m)  kubelet            MountVolume.SetUp failed for volume &#34;metrics-proxy-tls-config&#34; : secret &#34;metrics-proxy-tls-config&#34; not found
</span></span><span class="line"><span class="cl">  Warning  FailedMount  3m12s (x14 over 15m)  kubelet            MountVolume.SetUp failed for volume &#34;telegraf-configs&#34; : secret &#34;metrics-proxy-http-config&#34; not found
</span></span></code></pre></div><p>Nothing creates them. The package doesn&rsquo;t. The cluster doesn&rsquo;t. The
PackageInstall reports <code>ReconcileFailed</code> and backs off, and there it
stays, waiting for two secrets that aren&rsquo;t coming.</p>
<h2 id="the-supported-route-first">The supported route first</h2>
<p>On VCF 9.1, VKS add-on management installs Telegraf and Prometheus for
you. Broadcom&rsquo;s page on enabling monitoring for VKS clusters (linked at
the end) puts it plainly: &ldquo;After the prerequisites are met, any new VKS
cluster is automatically monitored.&rdquo; It speaks only of new clusters. The
prerequisites, and how to check each:</p>
<ol>
<li><strong>VCF Operations monitors the vCenter and its Supervisor</strong>:
<em>Enable vSphere Supervisor Collection</em> in the vCenter account&rsquo;s
Advanced Settings, true by default. Look for a vSphere Supervisor
adapter instance and VKS Cluster objects in the inventory.</li>
<li><strong>The Metrics Aggregator runs on the Supervisor.</strong> VCF Automation
installs it by default. Check the Supervisor&rsquo;s services in vCenter.</li>
<li><strong>The add-on repository offers Telegraf and Prometheus</strong>:
<code>AddonRepository</code>, <code>Addon</code> and <code>AddonConfigDefinition</code> resources on
the Supervisor.</li>
</ol>
<p>On 9.0.1 and later, Tom Fojta&rsquo;s <a href="https://fojta.wordpress.com/2026/02/03/monitoring-vks-cluster-in-vcf-automation/">Monitoring VKS Cluster in VCF
Automation</a>
installs the same two packages as add-on tiles in VCF Automation, once
the provider enables the Supervisor Management Proxy. His notes include a
Telegraf failure over an existing <code>metrics-proxy-tls-config</code> secret: the
mirror image of the one above.</p>
<p>f06 took neither route on 23 August. It ran 9.1, but its VCF Automation
arrived the next day. Ops had a vSphere Supervisor adapter instance, the
Metrics Aggregator wasn&rsquo;t registered, and nobody looked for add-ons. By
nobody, I mean me. The lab&rsquo;s catalog item installed the standard package
itself, following Christian Ferber&rsquo;s <a href="https://vrealize.it/2025/10/24/monitoring-vks-clusters-in-vcf-operations/">vrealize.it
write-up</a>
for 9.0.1. That path is the rest of this post.</p>
<p>Two days later, with VCF Automation deployed and the Supervisor rebuilt,
the automatic route left a footprint. A cluster created through VCF
Automation&rsquo;s API had Telegraf and Prometheus namespaces within two
minutes, none of it mine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">NAME                     STATUS   ROLES           AGE    VERSION
</span></span><span class="line"><span class="cl">vks-demo01-jn7xr-2gkq9   Ready    control-plane   111s   v1.35.5+vmware.1
</span></span><span class="line"><span class="cl">--- namespaces ---
</span></span><span class="line"><span class="cl">NAME                                 STATUS   AGE
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">tanzu-system-monitoring              Active   18s
</span></span><span class="line"><span class="cl">tanzu-system-telegraf                Active   11s
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>The rebuilt demo01 had them too, before the catalog item&rsquo;s package stage
ran. So the item&rsquo;s PackageInstalls collided with another kapp app:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">=== telegraf ===
</span></span><span class="line"><span class="cl">kapp: Error: Ownership errors: - Resource &#39;clusterrole/telegraf-kubelet-metric-access (rbac.authorization.k8s.io/v1) cluster&#39; is already associated with a different label &#39;kapp.k14s.io/app=1787648814041772277&#39; - Resource &#39;serviceaccount/telegraf-sa (v1) namespace: tanzu-system-telegraf&#39; is already a
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>I didn&rsquo;t inspect what installed them (the Supervisor was serving the
<code>addons.kubernetes.vmware.com</code> API by then). It&rsquo;s a footprint, not a
test. Check for Telegraf before you install it.</p>
<h2 id="what-sits-underneath">What sits underneath</h2>
<p>The two secrets come from the <strong>Supervisor Management Proxy</strong>, a
Supervisor Service on the Supervisor, not in the guest. It runs envoy
behind a load balancer on port 10093.</p>
<p>Each guest cluster gets a headless Service pointing at it
(<code>supervisor-management-proxy</code> in <code>default</code>), plus the two secrets in
<code>kube-system</code>, each with a <code>SecretExport</code>. The package&rsquo;s <code>SecretImport</code>s
copy them into <code>tanzu-system-telegraf</code>, and Telegraf posts to
<code>https://supervisor-management-proxy.default.svc.&lt;serviceDomain&gt;:10093/arc/tkgs/metric</code>.</p>
<p><img alt="Diagram: the secrets, the headless Service and the proxy&rsquo;s load balancer on f06, with the onward path to VCF Operations dashed" loading="lazy" src="/images/telegraf-vks-management-proxy-diagram.svg">
<em>Solid: what f06 showed. Dashed: the 9.1 design, in which the Supervisor also issues Telegraf&rsquo;s certificates and cert-manager rotates them.</em></p>
<p>The manual path needs a Supervisor with a load balancer, registry access
to <code>projects.packages.broadcom.com</code>, vCenter rights to register
Supervisor Services, and cluster-admin in the guest. The cluster is
<code>demo01</code>: one control-plane node and two workers.</p>
<h2 id="1-install-the-supervisor-management-proxy">1. Install the Supervisor Management Proxy</h2>
<p>The lab&rsquo;s catalog item that deploys a Supervisor has an
<code>installMgmtProxy</code> tick box: register the service, then install it.
Every attempt was refused. The definition shows why:</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">data.packaging.carvel.dev/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">Package</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></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">supervisor-management-proxy.vmware.com.0.4.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">appplatform.vmware.com/use-system-vpc</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">appplatform.vmware.com/required_capability</span><span class="p">:</span><span class="w"> </span><span class="l">Load_Balancer_Supported</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">appplatform.vmware.com/vcenter-version-constraints</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&gt;=9.0.0&#39;</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">refName</span><span class="p">:</span><span class="w"> </span><span class="l">supervisor-management-proxy.vmware.com</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="m">0.4.1</span><span class="w">
</span></span></span></code></pre></div><p>The proxy asks for the <strong>system VPC</strong>. vCenter only places services there
that it can verify as published by Broadcom:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">=== install on supervisor domain-c9 ===
</span></span><span class="line"><span class="cl">  install failed: 500
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">      &#34;default_message&#34;: &#34;Service supervisor-management-proxy.vmware.com version 0.4.1 is not a trusted service published by Broadcom and cannot be placed on the system VPC.&#34;,
</span></span><span class="line"><span class="cl">      &#34;id&#34;: &#34;vcenter.wcp.appplatform.signature_verification.system_vpc&#34;
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Registered through the API as Carvel YAML, it never passed, even
byte-identical to Broadcom&rsquo;s download. The other Carvel services here
carry no such annotation and installed fine:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">harbor                       2.14.2+vmware.2-vks.2    no system-vpc annotation  content_type=CARVEL_APPS_YAML
</span></span><span class="line"><span class="cl">cci-ns                       9.1.0-embedded+739b5075  no system-vpc annotation  content_type=CARVEL_APPS_YAML
</span></span><span class="line"><span class="cl">velero                       1.9.0-embedded+25369333  no system-vpc annotation  content_type=CARVEL_APPS_YAML
</span></span></code></pre></div><p>That check is doing its job, and I won&rsquo;t show how the lab got past it.
On a platform you care about, install the proxy the way Broadcom&rsquo;s
proxy page (linked at the end) describes, so vCenter can verify what it
places on the system VPC. However it goes in, <strong>check it</strong> on the
Supervisor:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">30</span><span class="n">s</span><span class="p">]</span> <span class="n">service</span><span class="o">=</span><span class="n">CONFIGURED</span>  <span class="n">pod</span><span class="o">=</span><span class="n">Running</span> <span class="n">ready</span><span class="o">=</span><span class="mi">1</span><span class="o">/</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="o">---</span> <span class="n">LB</span> <span class="n">services</span> <span class="ow">in</span> <span class="n">proxy</span> <span class="n">ns</span> <span class="o">---</span>
</span></span><span class="line"><span class="cl">  <span class="n">workload</span><span class="o">-</span><span class="n">metrics</span><span class="o">-</span><span class="n">loadbalancer</span><span class="p">:</span> <span class="n">type</span><span class="o">=</span><span class="n">LoadBalancer</span> <span class="n">ip</span><span class="o">=</span><span class="mf">10.26</span><span class="o">.</span><span class="mf">20.21</span> <span class="n">ports</span><span class="o">=</span><span class="mi">10093</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span></code></pre></div><p>Then check it in vCenter. The install record holds only the namespace the
platform chose (the base64 is <code>namespace: svc-supervisor-management-proxy-acqyd</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">--- current install record ---
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  &#34;desired_version&#34;: &#34;0.4.1&#34;,
</span></span><span class="line"><span class="cl">  &#34;current_version&#34;: &#34;0.4.1&#34;,
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">        &#34;default_message&#34;: &#34;Reason: ReconcileSucceeded. Message: Reconcile succeeded.&#34;,
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">  &#34;yaml_service_config&#34;: &#34;bmFtZXNwYWNlOiBzdmMtc3VwZXJ2aXNvci1tYW5hZ2VtZW50LXByb3h5LWFjcXlkCg==&#34;,
</span></span><span class="line"><span class="cl">  &#34;service_namespace&#34;: &#34;svc-supervisor-management-proxy-acqyd&#34;,
</span></span><span class="line"><span class="cl">  &#34;display_name&#34;: &#34;Supervisor Management Proxy&#34;,
</span></span><span class="line"><span class="cl">  &#34;config_status&#34;: &#34;CONFIGURED&#34;
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h2 id="2-create-the-cluster-with-servicedomain">2. Create the cluster with serviceDomain</h2>
<p><code>clusterNetwork.serviceDomain</code> can&rsquo;t be added later (step 7). The catalog
item now sets it on every demo cluster. Here&rsquo;s demo01 as rebuilt:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  &#34;clusterNetwork&#34;: {
</span></span><span class="line"><span class="cl">    &#34;pods&#34;: {
</span></span><span class="line"><span class="cl">      &#34;cidrBlocks&#34;: [
</span></span><span class="line"><span class="cl">        &#34;192.168.0.0/16&#34;
</span></span><span class="line"><span class="cl">      ]
</span></span><span class="line"><span class="cl">    },
</span></span><span class="line"><span class="cl">    &#34;serviceDomain&#34;: &#34;cluster.local&#34;,
</span></span><span class="line"><span class="cl">    &#34;services&#34;: {
</span></span><span class="line"><span class="cl">      &#34;cidrBlocks&#34;: [
</span></span><span class="line"><span class="cl">        &#34;10.96.0.0/12&#34;
</span></span><span class="line"><span class="cl">      ]
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  },
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><h2 id="3-add-the-package-repository">3. Add the package repository</h2>
<p>Telegraf ships in Broadcom&rsquo;s VKS standard packages. Register the
repository in the guest&rsquo;s <code>tkg-system</code> 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="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">packaging.carvel.dev/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">PackageRepository</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></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">broadcom-standard-repo</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">tkg-system</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">fetch</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">imgpkgBundle</span><span class="p">:</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="l">projects.packages.broadcom.com/vsphere/supervisor/packages/2025.8.19/vks-standard-packages:v2025.8.19</span><span class="w">
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">packagerepository.packaging.carvel.dev/broadcom-standard-repo created
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">NAME                     AGE   DESCRIPTION           PAUSED
</span></span><span class="line"><span class="cl">broadcom-standard-repo   60s   Reconcile succeeded
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">=== packages available ===
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">NAME                                                                  PACKAGEMETADATA NAME                             VERSION                  AGE
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">telegraf.kubernetes.vmware.com.1.34.4+vmware.2-vks.1                  telegraf.kubernetes.vmware.com                   1.34.4+vmware.2-vks.1    54s
</span></span><span class="line"><span class="cl">telegraf.tanzu.vmware.com.1.32.1+vmware.1-tkg.1                       telegraf.tanzu.vmware.com                        1.32.1+vmware.1-tkg.1    54s
</span></span></code></pre></div><h2 id="4-install-telegraf">4. Install Telegraf</h2>
<p>Two values matter, from the package&rsquo;s own schema:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="o">---</span><span class="w"> </span><span class="nx">telegraf</span><span class="w"> </span><span class="kn">package</span><span class="w"> </span><span class="nx">valuesSchema</span><span class="w"> </span><span class="nx">keys</span><span class="w"> </span><span class="o">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nf">domainName</span><span class="w">  </span><span class="p">(</span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="nx">cluster</span><span class="p">.</span><span class="nx">local</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nf">isMetricProxyConfigured</span><span class="w">  </span><span class="p">(</span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="nx">False</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nf">namespace</span><span class="w">  </span><span class="p">(</span><span class="k">default</span><span class="p">:</span><span class="w"> </span><span class="nx">tanzu</span><span class="o">-</span><span class="nx">system</span><span class="o">-</span><span class="nx">telegraf</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="o">...</span><span class="w">
</span></span></span></code></pre></div><p>The catalog item installs it the Carvel way: a values Secret, then a
PackageInstall, in a <code>package-installs</code> namespace whose service account
has cluster-admin.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">pkgSecret</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">valuesYml</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">k8sApply</span><span class="p">(</span><span class="nx">gk</span><span class="p">,</span> <span class="nx">gTok</span><span class="p">,</span> <span class="s2">&#34;/api/v1/namespaces/package-installs/secrets&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">apiVersion</span><span class="o">:</span> <span class="s2">&#34;v1&#34;</span><span class="p">,</span> <span class="nx">kind</span><span class="o">:</span> <span class="s2">&#34;Secret&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">metadata</span><span class="o">:</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">namespace</span><span class="o">:</span> <span class="s2">&#34;package-installs&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">stringData</span><span class="o">:</span> <span class="p">{</span> <span class="s2">&#34;values.yml&#34;</span><span class="o">:</span> <span class="nx">valuesYml</span> <span class="p">}</span> <span class="p">},</span> <span class="s2">&#34;secret &#34;</span> <span class="o">+</span> <span class="nx">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">pkgInstall</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">refName</span><span class="p">,</span> <span class="nx">ver</span><span class="p">,</span> <span class="nx">valuesSecret</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">...</span>
</span></span><span class="line"><span class="cl">        <span class="kd">var</span> <span class="nx">pi</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">apiVersion</span><span class="o">:</span> <span class="s2">&#34;packaging.carvel.dev/v1alpha1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">kind</span><span class="o">:</span> <span class="s2">&#34;PackageInstall&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">metadata</span><span class="o">:</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">namespace</span><span class="o">:</span> <span class="s2">&#34;package-installs&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">spec</span><span class="o">:</span> <span class="p">{</span> <span class="nx">serviceAccountName</span><span class="o">:</span> <span class="s2">&#34;pkgi-sa&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="p">...</span>
</span></span><span class="line"><span class="cl">                <span class="nx">packageRef</span><span class="o">:</span> <span class="p">{</span> <span class="nx">refName</span><span class="o">:</span> <span class="nx">refName</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nx">versionSelection</span><span class="o">:</span> <span class="p">{</span> <span class="nx">constraints</span><span class="o">:</span> <span class="nx">ver</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">valuesSecret</span><span class="p">)</span> <span class="p">{</span> <span class="nx">pi</span><span class="p">.</span><span class="nx">spec</span><span class="p">.</span><span class="nx">values</span> <span class="o">=</span> <span class="p">[{</span> <span class="nx">secretRef</span><span class="o">:</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="nx">valuesSecret</span> <span class="p">}</span> <span class="p">}];</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pkgSecret</span><span class="p">(</span><span class="s2">&#34;telegraf-values&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;domainName: cluster.local\n&#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;isMetricProxyConfigured: true\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pkgInstall</span><span class="p">(</span><span class="s2">&#34;telegraf&#34;</span><span class="p">,</span> <span class="s2">&#34;telegraf.kubernetes.vmware.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;1.34.4+vmware.2-vks.1&#34;</span><span class="p">,</span> <span class="s2">&#34;telegraf-values&#34;</span><span class="p">);</span>
</span></span></code></pre></div><p>The pods land in <code>tanzu-system-telegraf</code>: one per node from a DaemonSet,
plus one Deployment pod.</p>
<h2 id="5-watch-the-secrets-arrive">5. Watch the secrets arrive</h2>
<p>This is the dependency. Within two minutes of the proxy reporting
<code>CONFIGURED</code>, the secrets and their <code>SecretExport</code>s were in demo01&rsquo;s
<code>kube-system</code>. The <code>SecretImport</code>s had copied them across:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">---</span> <span class="n">secrets</span> <span class="o">+</span> <span class="n">secretimports</span> <span class="ow">in</span> <span class="n">tanzu</span><span class="o">-</span><span class="n">system</span><span class="o">-</span><span class="n">telegraf</span> <span class="o">---</span>
</span></span><span class="line"><span class="cl">  <span class="n">secrets</span><span class="p">:</span> <span class="n">metrics</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">http</span><span class="o">-</span><span class="n">config</span><span class="p">,</span> <span class="n">metrics</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">tls</span><span class="o">-</span><span class="n">config</span><span class="p">,</span> <span class="n">telegraf</span><span class="o">-</span><span class="n">registry</span><span class="o">-</span><span class="n">creds</span>
</span></span><span class="line"><span class="cl">  <span class="n">secretimport</span><span class="p">:</span> <span class="n">metrics</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">http</span><span class="o">-</span><span class="n">config</span> <span class="o">-&gt;</span> <span class="n">ReconcileSucceeded</span><span class="o">=</span><span class="n">True</span>
</span></span><span class="line"><span class="cl">  <span class="n">secretimport</span><span class="p">:</span> <span class="n">metrics</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">tls</span><span class="o">-</span><span class="n">config</span> <span class="o">-&gt;</span> <span class="n">ReconcileSucceeded</span><span class="o">=</span><span class="n">True</span>
</span></span><span class="line"><span class="cl">  <span class="n">kube</span><span class="o">-</span><span class="n">system</span> <span class="n">secretexport</span><span class="p">:</span> <span class="n">metrics</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">http</span><span class="o">-</span><span class="n">config</span> <span class="n">toNamespaces</span><span class="o">=</span>
</span></span><span class="line"><span class="cl">  <span class="n">kube</span><span class="o">-</span><span class="n">system</span> <span class="n">secretexport</span><span class="p">:</span> <span class="n">metrics</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">tls</span><span class="o">-</span><span class="n">config</span> <span class="n">toNamespaces</span><span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span></code></pre></div><p>The pods came up on their own:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">  [30s] telegraf pods ready: 0/4  pkgi: ReconcileFailed=True
</span></span><span class="line"><span class="cl">  [60s] telegraf pods ready: 2/4  pkgi: ReconcileFailed=True
</span></span><span class="line"><span class="cl">  [90s] telegraf pods ready: 4/4  pkgi: ReconcileFailed=True
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">  [480s] telegraf pods ready: 4/4  pkgi: ReconcileFailed=True
</span></span></code></pre></div><p>After an hour and forty minutes of <code>FailedMount</code>, all four were Running
four minutes after the proxy went <code>CONFIGURED</code>. I&rsquo;d quite like that hour
and forty minutes back.</p>
<h2 id="6-clear-the-stale-backoff">6. Clear the stale backoff</h2>
<p>The PackageInstall stayed in its <code>ReconcileFailed</code> backoff with every pod
Running. Bumping an annotation forces a fresh reconcile:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$K</span><span class="p">=</span><span class="vm">@</span><span class="p">{</span><span class="n">Authorization</span><span class="p">=</span><span class="s2">&#34;Bearer </span><span class="p">$(</span><span class="nv">$lg</span><span class="p">.</span><span class="n">session_id</span><span class="p">)</span><span class="s2">&#34;</span><span class="p">;</span> <span class="s1">&#39;Content-Type&#39;</span><span class="p">=</span><span class="s1">&#39;application/merge-patch+json&#39;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="nv">$u</span><span class="p">=</span><span class="s2">&#34;https://</span><span class="nv">${gk}</span><span class="s2">:6443/apis/packaging.carvel.dev/v1alpha1/namespaces/package-installs/packageinstalls/telegraf&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="c"># force re-reconcile by bumping an annotation</span>
</span></span><span class="line"><span class="cl"><span class="nv">$patch</span><span class="p">=</span><span class="s1">&#39;{&#34;metadata&#34;:{&#34;annotations&#34;:{&#34;lab.kick&#34;:&#34;&#39;</span><span class="p">+(</span><span class="nb">Get-Random</span><span class="p">)+</span><span class="s1">&#39;&#34;}}}&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="n">-Method</span> <span class="n">Patch</span> <span class="n">-Uri</span> <span class="nv">$u</span> <span class="n">-Headers</span> <span class="nv">$K</span> <span class="n">-Body</span> <span class="nv">$patch</span> <span class="n">-SkipCertificateCheck</span> <span class="p">|</span> <span class="nb">Out-Null</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">kicked; polling...
</span></span><span class="line"><span class="cl">  [30s] ReconcileSucceeded=True Reconcile succeeded
</span></span></code></pre></div><h2 id="7-check-the-output-url">7. Check the output URL</h2>
<p>Telegraf still wasn&rsquo;t delivering. Its log, one error a minute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">  2026-08-23T19:17:56Z I! [agent] Config: Interval:5m0s, Quiet:false, Hostname:&#34;telegraf-7cd76f96-wbthp&#34;, Flush Interval:1m0s
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">  2026-08-23T19:20:03Z E! [agent] Error writing to outputs.http: Post &#34;https://supervisor-management-proxy.default.svc.:10093/arc/tkgs/metric&#34;: dial tcp: lookup supervisor-management-proxy.default.svc. on 10.96.0.10:53: no such host
</span></span><span class="line"><span class="cl">  2026-08-23T19:20:56Z E! [agent] Error writing to outputs.http: Post &#34;https://supervisor-management-proxy.default.svc.:10093/arc/tkgs/metric&#34;: dial tcp: lookup supervisor-management-proxy.default.svc. on 10.96.0.10:53: no such host
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Note the trailing dot, and nothing after <code>svc</code>. The name is that headless
Service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">--- guest &#39;default&#39; ns services ---
</span></span><span class="line"><span class="cl">  kubernetes: type=ClusterIP extName= clusterIP=10.96.0.1
</span></span><span class="line"><span class="cl">  supervisor: type=ClusterIP extName= clusterIP=None
</span></span><span class="line"><span class="cl">  supervisor-management-proxy: type=ClusterIP extName= clusterIP=None
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">--- endpoints of supervisor-management-proxy (default ns) ---
</span></span><span class="line"><span class="cl">  ips=10.26.20.21 ports=10093
</span></span></code></pre></div><p>The domain part follows the cluster&rsquo;s <code>serviceDomain</code>, which demo01
lacked. The package&rsquo;s own <code>domainName</code> was <code>cluster.local</code> all along, and
made no difference. Adding the field later was refused:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">patch rejected:  { &#34;kind&#34;: &#34;Status&#34;, &#34;apiVersion&#34;: &#34;v1&#34;, &#34;metadata&#34;: {}, &#34;status&#34;: &#34;Failure&#34;, &#34;message&#34;: &#34;admission webhook \u0022capi.validating.tanzukubernetescluster.run.tanzu.vmware.com\u0022 denied the request: spec.clusterNetwork is immutable and cannot be upd
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>Two fixes, both applied:</p>
<ul>
<li><strong>Every new cluster:</strong> <code>serviceDomain: cluster.local</code> in the spec
(step 2). Every add-on that builds a service URL assumes it&rsquo;s there.</li>
<li><strong>Live cluster:</strong> a CoreDNS <code>rewrite</code> rule in the guest&rsquo;s <code>coredns</code>
ConfigMap, mapping the domainless name onto the real one. Ugly,
effective, and documented in the cluster&rsquo;s notes. The <code>reload</code> plugin
picked it up:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">--- Corefile now ---
</span></span><span class="line"><span class="cl">.:53 {
</span></span><span class="line"><span class="cl">    errors
</span></span><span class="line"><span class="cl">    health {
</span></span><span class="line"><span class="cl">       lameduck 5s
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">    ready
</span></span><span class="line"><span class="cl">    rewrite name supervisor-management-proxy.default.svc supervisor-management-proxy.default.svc.cluster.local
</span></span><span class="line"><span class="cl">    kubernetes cluster.local in-addr.arpa ip6.arpa {
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><h2 id="8-leave-the-proxys-values-alone">8. Leave the proxy&rsquo;s values alone</h2>
<p>The error moved on, which counts as progress around here. The name resolves now,
and nothing answers:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">checked at (UTC): 19:35:38
</span></span><span class="line"><span class="cl">telegraf-26ppl: STILL FAILING (4 errors)
</span></span><span class="line"><span class="cl">  last: 2026-08-23T19:35:07Z E! [agent] Error writing to outputs.http: Post &#34;https://supervisor-management-proxy.default.svc.:10093/arc/tkgs/metric&#34;: context deadline exceeded (Client.Timeout exceeded while awaiting headers)
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>In 9.1 terms, a prerequisite was missing. The Metrics Aggregator, which
terminates the cluster&rsquo;s TLS in that design, didn&rsquo;t exist on f06 yet. I
tried pointing the proxy at Ops by hand, with a value from its
definition:</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">metricsHTTPRemoteEndpoint</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="l">HTTP remote endpoint configuration for pushing workload metrics</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">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">host</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">port</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">default</span><span class="p">:</span><span class="w"> </span><span class="m">443</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">tlsClientSecretName</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">description</span><span class="p">:</span><span class="w"> </span><span class="l">Name of the secret which contains the TLS configuration required to push metrics to remote endpoint.</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="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">tlsClientSecretNamespace</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><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">default</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;vmware-system-monitoring&#34;</span><span class="w">
</span></span></span></code></pre></div><p>A <code>PUT</code> on the install record (<code>PATCH</code> returns 404) reconciled:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$vals</span><span class="p">=</span><span class="s2">&#34;namespace: svc-supervisor-management-proxy-acqyd</span><span class="se">`n</span><span class="s2">metricsHTTPRemoteEndpoint:</span><span class="se">`n</span><span class="s2">  host: </span><span class="se">`&#34;</span><span class="s2">f06-flt-ops01.res.lab</span><span class="se">`&#34;`n</span><span class="s2">  port: 443</span><span class="se">`n</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$b64</span><span class="p">=[</span><span class="no">Convert</span><span class="p">]::</span><span class="n">ToBase64String</span><span class="p">([</span><span class="no">Text.Encoding</span><span class="p">]::</span><span class="n">UTF8</span><span class="p">.</span><span class="py">GetBytes</span><span class="p">(</span><span class="nv">$vals</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nv">$body</span><span class="p">=</span><span class="vm">@</span><span class="p">{</span> <span class="n">version</span><span class="p">=</span><span class="s1">&#39;0.4.1&#39;</span><span class="p">;</span> <span class="n">yaml_service_config</span><span class="p">=</span><span class="nv">$b64</span> <span class="p">}</span> <span class="p">|</span> <span class="nb">ConvertTo-Json</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="nv">$r</span><span class="p">=</span><span class="nb">Invoke-WebRequest</span> <span class="n">-Method</span> <span class="n">Put</span> <span class="n">-Uri</span> <span class="s2">&#34;https://</span><span class="nv">$vc</span><span class="s2">/api/vcenter/namespace-management/clusters/domain-c9/supervisor-services/</span><span class="nv">$svc</span><span class="s2">&#34;</span> <span class="n">-Headers</span> <span class="nv">$H</span> <span class="n">-ContentType</span> <span class="s1">&#39;application/json&#39;</span> <span class="n">-Body</span> <span class="nv">$body</span> <span class="n">-SkipCertificateCheck</span> <span class="n">-TimeoutSec</span> <span class="mf">120</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">PUT -&gt; 204
</span></span><span class="line"><span class="cl">  [30s] CONFIGURED
</span></span><span class="line"><span class="cl">  [60s] CONFIGURED
</span></span><span class="line"><span class="cl">  [90s] CONFIGURED
</span></span></code></pre></div><p>Telegraf timed out as before. Broadcom&rsquo;s proxy page is blunt: &ldquo;No
additional configuration values for the Supervisor Management Proxy
service are required in this case.&rdquo;</p>
<p>Set at install time, the value did harm. With no <code>tlsClientSecretName</code>,
the package renders a nameless <code>SecretImport</code>, and kapp refuses it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">=== supervisor-management-proxy.vmware.com ===
</span></span><span class="line"><span class="cl">Reason: ReconcileFailed. Message: kapp: Error: Validation errors:
</span></span><span class="line"><span class="cl">- Expected &#39;metadata.name&#39; on resource &#39;secretimport/ (secretgen.carvel.dev/v1alpha1) namespace: svc-supervisor-management-proxy-htarg&#39; to be non-empty (stdin doc 5).
</span></span></code></pre></div><p>The next day, the Supervisor was rebuilt onto the lab&rsquo;s planned address
range. The catalog item installed the proxy and the Metrics Aggregator
with no values, and both went <code>CONFIGURED</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">---</span> <span class="n">ALL</span> <span class="n">LoadBalancer</span> <span class="n">VIPs</span> <span class="n">on</span> <span class="n">the</span> <span class="n">rebuilt</span> <span class="n">supervisor</span> <span class="o">---</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">svc</span><span class="o">-</span><span class="n">metrics</span><span class="o">-</span><span class="n">aggregator</span><span class="o">-</span><span class="mi">5</span><span class="n">q0of</span>             <span class="n">workload</span><span class="o">-</span><span class="n">metrics</span><span class="o">-</span><span class="n">loadbalancer</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.7</span>
</span></span><span class="line"><span class="cl">  <span class="n">svc</span><span class="o">-</span><span class="n">supervisor</span><span class="o">-</span><span class="n">management</span><span class="o">-</span><span class="n">proxy</span><span class="o">-</span><span class="n">htarg</span>    <span class="n">workload</span><span class="o">-</span><span class="n">metrics</span><span class="o">-</span><span class="n">loadbalancer</span> <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.6</span>
</span></span></code></pre></div><p>demo01 was recreated on that Supervisor with <code>serviceDomain</code> set. This
check read a Telegraf pod&rsquo;s last two minutes of log. The broken state had
logged an error a minute:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">=== observability packages ===
</span></span><span class="line"><span class="cl">  cert-manager   ReconcileSucceeded=True
</span></span><span class="line"><span class="cl">  contour        Reconciling=True
</span></span><span class="line"><span class="cl">  fluent-bit     ReconcileSucceeded=True
</span></span><span class="line"><span class="cl">  prometheus     ReconcileFailed=True
</span></span><span class="line"><span class="cl">  telegraf       ReconcileFailed=True
</span></span><span class="line"><span class="cl">=== telegraf delivery (last 2 min) ===
</span></span><span class="line"><span class="cl">  CLEAN - metrics flowing to mgmt proxy
</span></span></code></pre></div><p>&ldquo;Metrics flowing&rdquo; is my script&rsquo;s wording for no failed writes. And the
<code>telegraf</code> PackageInstall is the one kapp refused, so the clean Telegraf
wasn&rsquo;t mine.</p>
<h2 id="9-vcf-operations">9. VCF Operations</h2>
<p>Broadcom&rsquo;s consumption docs show the result in VCF Automation (Manage
and Govern &gt; Kubernetes Management &gt; Clusters) and in a Workload
Management Activated Cluster Summary tab in VCF Operations. The 9.0.1
write-up adds a switch on the VKS Cluster object, which the catalog item
still names at the end of each run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">... | Ops manual step: set &#39;Pod And Container Monitoring Enabled&#39; on VKS Cluster demo01 in VCF Operations inventory; ...
</span></span></code></pre></div><p>This record has neither. The switch was never set, and my one capture
of a VKS Cluster object (vks-demo01, 1 September, in the <a href="/posts/vks-kubectl-vs-vcfa-all-apps/">two-ways
post</a>) counts 0 pods, deployments
and DaemonSets. The last hop I can show is Telegraf writing without
errors. Not the grand finale I&rsquo;d hoped for, but an honest one.</p>
<h2 id="clean-up-and-repeatability">Clean-up and repeatability</h2>
<ul>
<li><strong>One owner per add-on.</strong> Where add-on management installs Telegraf,
don&rsquo;t add your own PackageInstall. To run it yourself, first set the
cluster label <code>addons.kubernetes.vmware.com/automated-monitoring</code> to
<code>disabled</code>. The catalog item&rsquo;s answer to the ownership errors,
<code>--dangerous-override-ownership-of-existing-resources=true</code>, only hands
the objects to a second owner.</li>
<li><strong>Supervisor Service API on 9.1:</strong> service
<code>carvel_spec.version_spec.content</code>, version <code>carvel_spec.content</code>,
install <code>{supervisor_service, version}</code>, values
<code>PUT {version, yaml_service_config}</code>, removal
<code>PATCH …?action=deactivate</code>, then <code>DELETE</code>. New definition bytes need a
new service record.</li>
<li><strong>A definition outlives the Supervisor.</strong> What you register lives on
vCenter&rsquo;s service record, so a rebuilt Supervisor installs the same
definition again. Replacing it means deactivating and deleting that
record first.</li>
<li><strong>Services break later.</strong> In September, the proxy, the aggregator and
four other Supervisor Services went to <code>ERROR</code> when the platform
replaced their kapp service accounts. Recreating the old ones fixed all
six within two minutes.</li>
</ul>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>On VCF 9.1, Kubernetes metrics in VCF Operations are meant to be a
platform setting, not a job per cluster. Get three prerequisites right,
and new clusters arrive monitored.</p>
<p>The layer underneath still matters, because every failure here had the
same shape: a generic symptom (<code>FailedMount</code>, a name that won&rsquo;t resolve,
a timeout) caused by a decision made somewhere else.</p>
<p>For a customer, that means a Supervisor built for observability before
any cluster asks, a cluster baseline with the fields add-ons assume, and
one owner per add-on. That&rsquo;s exactly what a standard cluster class and a
Supervisor build checklist exist to encode.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>On 9.1, start with the supported route: Ops collecting the Supervisor,
the Metrics Aggregator and the add-on repository. Then check for
Telegraf before installing it.</li>
<li>On VKS, the Telegraf package <strong>hard-depends on the Supervisor
Management Proxy</strong> whenever the metric proxy flag is set. <code>FailedMount</code>
on two <code>metrics-proxy-*</code> secrets is the tell.</li>
<li>The dependency heals retroactively: install the proxy, bump the
PackageInstall annotation, wait.</li>
<li>Set <code>serviceDomain</code> at cluster create. It&rsquo;s immutable, and every add-on
that builds a service URL will assume it.</li>
<li>A name that resolves isn&rsquo;t a metric delivered. Read the output log for
an error-free window, and leave the proxy&rsquo;s values alone.</li>
<li>When a package &ldquo;does nothing&rdquo;, describe the pod, not the package.</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/infrastructure-operations/connect-to-data-sources/vsphere-supervisor-monitoring/steps-to-monitor-vsphere-supervisor-clusters-and-resources/prerequisites-for-vsphere-supervisor-monitoring.html">Enabling Monitoring for VKS Clusters</a>:
the 9.1 route and its prerequisites.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/connect-to-data-sources/vsphere-supervisor-monitoring/steps-to-monitor-vsphere-supervisor-clusters-and-resources/enabling-the-vsphere-supervisor-collection.html">Enabling Monitoring for vSphere Supervisor</a>:
the Supervisor collection setting on the vCenter account.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/operating-tkg-service-clusters/monitoring-vks-clusters-using-vcf-operations.html">Monitoring VKS Clusters Using VCF Operations</a>:
the automated add-ons, the metrics-aggregator service, and where
metrics appear.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/operating-tkg-service-clusters/disable-automated-monitoring-on-vks-clusters-using-vcf-a.html">Disable Automated Monitoring on VKS Clusters Using VCF Automation</a>:
the per-cluster label, for clusters where you run Telegraf yourself.</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-service-administration-and-development/9-0/using-supervisor-services/install-the-supervisor-management-proxy-service.html">Using the Supervisor Management Proxy Service</a>:
what the proxy is for; no extra values for VCF Operations.</li>
</ul>
<p><em>Previously: <a href="/posts/telegraf-windows-2025/">Telegraf on Windows Server 2025</a>.
More in the <a href="/series/observability-on-vcf/">Observability on VCF</a> series.</em></p>
<hr>
<p><em>Lab environment; opinions my own.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>fluent-bit two ways: VKS add-on and Windows agent, one log endpoint</title>
      <link>https://thenestedlab.com/posts/fluent-bit-two-ways/</link>
      <pubDate>Wed, 16 Sep 2026 12:00:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/fluent-bit-two-ways/</guid>
      <description>One log pipeline, two very different worlds: fluent-bit as a VKS package and as a Windows Server 2025 service, both shipping to the same VCF Operations for Logs. One backend, two configs, lessons from both.</description>
      <content:encoded><![CDATA[<p>Logs from a Kubernetes cluster and logs from a Windows server end up in
the same place: VCF Operations for Logs. The roads there couldn&rsquo;t look
more different.</p>
<p>On VKS, fluent-bit is a <em>package</em>: declare a values secret, reconcile,
done. On Windows it&rsquo;s a <em>service</em>: install it, write a config by hand,
restart, tail the debug log. Same binary, same output plugin, same
endpoint. This post puts the two side by side, so the shared shape is
easy to see.</p>
<h2 id="the-endpoint">The endpoint</h2>
<p>VCF Operations for Logs ingests over its <strong>CFAPI</strong> on port 9543:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">https://&lt;ops-logs&gt;:9543/api/v2/events
</span></span></code></pre></div><p>Auth is <a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/log-analysis/overview-of-log-management-agents/agent-authentication-for-log-ingestion.html">enabled but optional by default</a>.
The endpoint accepts events with or without a token, unless strict
authentication is turned on in Global Settings. With that on, it drops any
request without a valid Bearer token. Our shippers sent no token, and the lab ran
the default.</p>
<p>What matters to fluent-bit is the URI, the port, TLS and a JSON body
shaped as <code>{&quot;events&quot;:[...]}</code>. Both shippers below produce exactly that.
Eventually.</p>
<h2 id="way-1-vks-package">Way 1: VKS package</h2>
<p>VKS ships fluent-bit in its standard package repository. On a cluster with
the repo registered:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">kubectl ... get packages -n tkg-system 2&gt;&amp;1 | Select-String -Pattern &#39;NAME|telegraf|prometheus|fluent|cert-manager|contour&#39; | Out-String
</span></span><span class="line"><span class="cl">NAME                                                                  PACKAGEMETADATA NAME                             VERSION                  AGE
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">fluent-bit.kubernetes.vmware.com.4.0.2+vmware.1-vks.1                 fluent-bit.kubernetes.vmware.com                 4.0.2+vmware.1-vks.1     55s
</span></span><span class="line"><span class="cl">fluent-bit.kubernetes.vmware.com.4.0.5+vmware.1-vks.1                 fluent-bit.kubernetes.vmware.com                 4.0.5+vmware.1-vks.1     55s
</span></span><span class="line"><span class="cl">fluent-bit.tanzu.vmware.com.2.2.3+vmware.1-tkg.2                      fluent-bit.tanzu.vmware.com                      2.2.3+vmware.1-tkg.2     56s
</span></span><span class="line"><span class="cl">fluent-bit.tanzu.vmware.com.3.1.9+vmware.1-tkg.1                      fluent-bit.tanzu.vmware.com                      3.1.9+vmware.1-tkg.1     55s
</span></span><span class="line"><span class="cl">fluent-bit.tanzu.vmware.com.3.2.7+vmware.1-tkg.1                      fluent-bit.tanzu.vmware.com                      3.2.7+vmware.1-tkg.1     55s
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>A values secret configures the output. The package&rsquo;s own default output
is already shaped for CFAPI, so the values are short:</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 class="l">tanzu-system-logging</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">fluent_bit</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">outputs</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">      [OUTPUT]
</span></span></span><span class="line"><span class="cl"><span class="sd">        Name          http
</span></span></span><span class="line"><span class="cl"><span class="sd">        Match         *
</span></span></span><span class="line"><span class="cl"><span class="sd">        Host          f06-flt-log01.res.lab
</span></span></span><span class="line"><span class="cl"><span class="sd">        Port          9543
</span></span></span><span class="line"><span class="cl"><span class="sd">        URI           api/v2/events
</span></span></span><span class="line"><span class="cl"><span class="sd">        Format        json
</span></span></span><span class="line"><span class="cl"><span class="sd">        tls.debug     4
</span></span></span><span class="line"><span class="cl"><span class="sd">        tls           on
</span></span></span><span class="line"><span class="cl"><span class="sd">        tls.verify    off
</span></span></span><span class="line"><span class="cl"><span class="sd">        json_date_key timestamp</span><span class="w">
</span></span></span></code></pre></div><p>Then a <code>PackageInstall</code> that points at it:</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">packaging.carvel.dev/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">PackageInstall</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="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="nt">fluent-bit, namespace</span><span class="p">:</span><span class="w"> </span><span class="l">package-installs }</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">serviceAccountName</span><span class="p">:</span><span class="w"> </span><span class="l">pkgi-sa</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">packageRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">refName</span><span class="p">:</span><span class="w"> </span><span class="l">fluent-bit.kubernetes.vmware.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">versionSelection</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">constraints</span><span class="p">:</span><span class="w"> </span><span class="m">4.0.5</span><span class="l">+vmware.1-vks.1 }</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">secretRef</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">fluent-bit-values }</span><span class="w">
</span></span></span></code></pre></div><p><code>Reconcile succeeded</code>. The proof is in the pod logs, not the Ops UI. Ops
for Logs federates its API authentication through the identity broker, so
a script has a far easier time checking from the shipper&rsquo;s side:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[ info] [output:http:http.0] f06-flt-log01.res.lab:9543, HTTP status=200
</span></span><span class="line"><span class="cl">[ info] [output:http:http.0] f06-flt-log01.res.lab:9543, HTTP status=200
</span></span></code></pre></div><p>One 200 per batch. That&rsquo;s the whole VKS side, and it almost felt like
cheating. It&rsquo;s also why the packaged path is the one to recommend. The
inputs (container logs, kubelet, systemd) come pre-wired, the parsers are
right, and the only decision you make is the output.</p>
<p><strong>One trap</strong> (shared with Telegraf, <a href="/series/observability-on-vcf/">next post</a>).
A cluster created without <code>clusterNetwork.serviceDomain</code> gives in-cluster
service names of the form <code>...svc.</code>, with no domain. Some add-ons build
those into URLs that don&rsquo;t resolve. The field is immutable after create,
so set it explicitly on every new cluster.</p>
<p><img alt="VCF Operations — Logs: text contains vks-demo01, last 24 h, 1.74K events" loading="lazy" src="/images/ui/l2-ops-logs-vks-demo01-query.jpg">
<em>The receiving end. One filter (<code>text contains vks-demo01</code>) and a day of the cluster&rsquo;s logs, one bar per hour.</em></p>
<p><img alt="The stream: container logs arriving with Kubernetes metadata — app, cluster, container, kubernetes_namespace, node, pod" loading="lazy" src="/images/ui/l1-ops-logs-vks-demo01-24h.jpg">
<em>Every event carries the fields the package&rsquo;s filters add: <code>cluster</code>, <code>kubernetes_namespace</code>, <code>pod</code> and <code>container</code>. That&rsquo;s what lets Windows and VKS sit side by side in one explorer later.</em></p>
<h2 id="way-2-windows-server-2025-service">Way 2: Windows Server 2025 service</h2>
<p>fluent-bit ships a Windows build with a <code>winevtlog</code> input. On the
<a href="/series/the-windows-build-pipeline/">pipeline-built W2025 server</a> I used the
portable zip rather than the MSI. No installer, just a folder under <code>C:\</code>
and a service registered with <code>sc.exe</code>.</p>
<p>A small script writes the config below (<code>$LogsHost</code> is the appliance&rsquo;s
FQDN, <code>$LogsPort</code> is 9543). Four of its lines turned out to matter:</p>
<details class="nl-fold">
<summary>The config the script writes: input, filter and output</summary>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[INPUT]</span>
</span></span><span class="line"><span class="cl">    <span class="na">Name          winevtlog</span>
</span></span><span class="line"><span class="cl">    <span class="na">Channels      System,Application,Security</span>
</span></span><span class="line"><span class="cl">    <span class="na">Interval_Sec  5</span>
</span></span><span class="line"><span class="cl">    <span class="na">DB            $root\winevt.db</span>
</span></span><span class="line"><span class="cl">    <span class="na">String_Inserts On</span>
</span></span><span class="line"><span class="cl">    <span class="na">Tag           winevt</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[FILTER]</span>
</span></span><span class="line"><span class="cl">    <span class="na">Name    modify</span>
</span></span><span class="line"><span class="cl">    <span class="na">Match   *</span>
</span></span><span class="line"><span class="cl">    <span class="na">Rename  Message  text</span>
</span></span><span class="line"><span class="cl">    <span class="na">Add     hostname $hostname</span>
</span></span><span class="line"><span class="cl">    <span class="na">Add     appname  v-windows</span>
</span></span><span class="line"><span class="cl">    <span class="na">Add     source   windows-server-2025</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[OUTPUT]</span>
</span></span><span class="line"><span class="cl">    <span class="na">Name    http</span>
</span></span><span class="line"><span class="cl">    <span class="na">Match   *</span>
</span></span><span class="line"><span class="cl">    <span class="na">Host    $LogsHost</span>
</span></span><span class="line"><span class="cl">    <span class="na">Port    $LogsPort</span>
</span></span><span class="line"><span class="cl">    <span class="na">URI     /api/v2/events</span>
</span></span><span class="line"><span class="cl">    <span class="na">Format  json</span>
</span></span><span class="line"><span class="cl">    <span class="na">Header  Content-Type application/json</span>
</span></span><span class="line"><span class="cl">    <span class="na">tls     On</span>
</span></span><span class="line"><span class="cl">    <span class="na">tls.verify Off</span>
</span></span><span class="line"><span class="cl">    <span class="na">json_date_key    timestamp</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ISO-8601 timestamps are rejected (400: not a valid Long); the API reads the number as epoch MILLISECONDS</span>
</span></span><span class="line"><span class="cl">    <span class="na">json_date_format epoch_ms</span>
</span></span><span class="line"><span class="cl">    <span class="na">net.dns.resolver LEGACY</span>
</span></span><span class="line"><span class="cl">    <span class="na">net.connect_timeout 20</span>
</span></span><span class="line"><span class="cl">    <span class="na">Retry_Limit      5</span>
</span></span></code></pre></div>
</details>

<p>The service came up first time. Getting a single event to <em>land</em> took four
attempts, and each failure taught me something the documentation doesn&rsquo;t
say:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[error] [output:http:http.0] no upstream connections available to f06-flt-log01.res.lab:9543
</span></span></code></pre></div><p><strong>1. fluent-bit couldn&rsquo;t resolve a name Windows could.</strong> <code>Resolve-DnsName</code>
worked. <code>Test-NetConnection … -Port 9543</code> worked. fluent-bit&rsquo;s own async
resolver didn&rsquo;t. <code>net.dns.resolver LEGACY</code> hands DNS back to the OS.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[error] [output:http:http.0] 10.26.5.25:9543, HTTP status=404
</span></span></code></pre></div><p><strong>2. The appliance routes on the Host header.</strong> Dodging DNS by using the
IP gets a 404 for <em>every</em> path. Address it by FQDN.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{&#34;errorMessage&#34;:&#34;Cannot deserialize value of type `java.lang.Long` from String \&#34;2026-09-16T10:55:00.000000Z\&#34;&#34;}
</span></span></code></pre></div><p><strong>3. Timestamps must be numeric.</strong> fluent-bit&rsquo;s <code>iso8601</code> output is a
string, and the ingest API wants a Long. Worse, it reads that number as
<strong>milliseconds</strong>. So on the Windows agent the default <code>double</code> (epoch
<em>seconds</em>) is accepted with a 200, and your events are filed in January
1970. That&rsquo;s a long way to scroll back. The fix is <code>epoch_ms</code>. Our VKS
values set no format at all, and those events land on time.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">{&#34;received&#34;:0,&#34;message&#34;:&#34;events ingested&#34;,&#34;status&#34;:&#34;ok&#34;}
</span></span></code></pre></div><p><strong>4. The message field must be called <code>text</code>.</strong> Anything else (<code>Message</code>
as <code>winevtlog</code> emits it, <code>message</code>, <code>log</code>) returns 200 and
<code>received: 0</code>. Silently dropped. Hence the <code>Rename</code> filter. Broadcom&rsquo;s own
<a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/log-analysis/overview-of-log-management-agents/install-fluent-bit-on-windows-server-for-vcf-operations-for-logs.html">reference config for Windows</a> carries exactly this line. I found it the hard
way first.</p>
<p>Then, finally:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[ info] [output:http:http.0] f06-flt-log01.res.lab:9543, HTTP status=200
</span></span><span class="line"><span class="cl">[ info] [output:http:http.0] f06-flt-log01.res.lab:9543, HTTP status=200
</span></span></code></pre></div><p><img alt="Ops for Logs: hostname contains TEST-W2025 — Windows System, Application and Security events, with channel, computer, eventid and appname as fields" loading="lazy" src="/images/ui/w3-ops-logs-test-w2025-events.jpg">
<em>Fifty events in the first five minutes: service state changes, a &ldquo;system time was changed&rdquo; security event with its full subject block, all searchable with the same fields as everything else.</em></p>
<p><img alt="The same explorer with both hostnames in one filter: TEST-W2025 and vks-demo01" loading="lazy" src="/images/ui/w4-ops-logs-windows-and-vks-filter.jpg">
<em>One filter, two worlds. A Windows server and a Kubernetes cluster, in the same query, in the same store.</em></p>
<p>One more Windows note. A bare <code>fluent-bit.exe</code> registered with <code>sc.exe</code>
isn&rsquo;t a proper service binary. It ignores the stop signal, and Windows
sits at &ldquo;waiting for service to stop&rdquo; until you kill the process. The MSI
installs a real service wrapper, so use it for anything that isn&rsquo;t a lab.</p>
<h2 id="the-shape-they-share">The shape they share</h2>
<table>
	<thead>
			<tr>
					<th></th>
					<th>VKS package</th>
					<th>Windows service</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Install</td>
					<td><code>PackageInstall</code></td>
					<td>MSI / <code>sc.exe create</code></td>
			</tr>
			<tr>
					<td>Config</td>
					<td>values Secret</td>
					<td><code>fluent-bit.conf</code></td>
			</tr>
			<tr>
					<td>Inputs</td>
					<td>pre-wired (containers, kubelet, systemd)</td>
					<td><code>winevtlog</code> channels you choose</td>
			</tr>
			<tr>
					<td>Output</td>
					<td><code>http</code> → CFAPI :9543 <code>api/v2/events</code></td>
					<td><code>http</code> → CFAPI :9543 <code>/api/v2/events</code>, <code>epoch_ms</code>, resolver and header lines</td>
			</tr>
			<tr>
					<td>Verify</td>
					<td>pod log <code>HTTP status=200</code></td>
					<td>service log <code>HTTP status=200</code> — and check the <em>date</em> on what arrived</td>
			</tr>
			<tr>
					<td>Restart safety</td>
					<td>Kubernetes</td>
					<td><code>DB</code> bookmark file</td>
			</tr>
	</tbody>
</table>
<p>Both outputs post JSON events to the same :9543 endpoint. They differ in
the URI form, the date handling (fluent-bit&rsquo;s default against <code>epoch_ms</code>)
and the resolver and header lines that only Windows carries. That&rsquo;s the
lesson. Standardise the <em>sink</em>, and let each platform own its <em>source</em>.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>The business outcome is one place to look. Windows servers, Kubernetes
clusters and the platform itself all ship logs to the same VCF Operations
for Logs, with the same fields, searchable in one query. So an incident
that spans a Windows service and a container gets investigated on one
screen, not across three tools.</p>
<p>Standardising the <em>destination</em>, while each platform keeps its native
shipper, is also what keeps the estate maintainable. There&rsquo;s one endpoint
to secure and retain, and no bespoke agent per team.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Ops for Logs ingestion is CFAPI on <code>:9543/api/v2/events</code>, in JSON. One
endpoint serves every shipper.</li>
<li>On VKS, use the <strong>package</strong> and decide only the output. Verify from the
pod log, because the Ops API is awkward to script against.</li>
<li>Set <code>serviceDomain</code> on every new VKS cluster. It&rsquo;s immutable.</li>
<li>On Windows: the FQDN, not the IP (Host-header routing),
<code>net.dns.resolver LEGACY</code>, <code>Rename Message text</code> and
<code>json_date_format epoch_ms</code>. Each one fails differently, and two of them
fail <em>silently</em>.</li>
<li>A <code>200</code> is not proof. <code>received: 0</code> is a drop, and a seconds timestamp
from the Windows agent is a 200 filed in 1970. Look for the event in the
explorer before you call it done, however sincere the 200 looks.</li>
<li>Prove &ldquo;one endpoint&rdquo; with one explorer view showing both sources.</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/installing-standard-packages-on-tkg-service-clusters/installing-standard-packages-on-tkg-cluster-using-tkr-for-vsphere-8-x/install-fluent-bit/install-fluent-bit-package.html">Install Fluent Bit Package</a>: the VKS standard package, <code>fluent-bit.kubernetes.vmware.com</code> from 4.0.x, installed with a data values file</li>
<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/installing-standard-packages-on-tkg-service-clusters/standard-package-reference/fluent-bit-package-reference.html">Fluent Bit Package Reference</a>: the package&rsquo;s values, with an <code>http</code> output to VCF Operations on port 9543</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/log-analysis/overview-of-log-management-agents/install-fluent-bit-on-windows-server-for-vcf-operations-for-logs.html">Set up the Windows system to collect logs in VCF Operations</a>: Broadcom&rsquo;s fluent-bit MSI and <code>winevtlog</code> config for Windows Server 2022 and 2025, <code>Rename Message text</code> and <code>epoch_ms</code> included</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/infrastructure-operations/log-analysis/overview-of-log-management-agents/agent-authentication-for-log-ingestion.html">Agent Authentication for Log Ingestion</a>: optional Bearer-token authentication on HTTP ingestion, fluent-bit included</li>
</ul>
<p><em>Next: <a href="/series/observability-on-vcf/">Telegraf on Windows Server 2025: unsupported, works anyway</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Seven demo apps, one request: deploying a showcase stack via VCF Automation</title>
      <link>https://thenestedlab.com/posts/demo-apps-via-vcfa/</link>
      <pubDate>Wed, 16 Sep 2026 08:40:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/demo-apps-via-vcfa/</guid>
      <description>Seven demo apps, Pac-Man and KubeDoom among them, on a VKS cluster that VCF Automation built in a tenant VPC, each with its own NSX virtual IP. The proper tenanted path, what you get for free, and the traps.</description>
      <content:encoded><![CDATA[<p>Every platform needs a demo stack: something that looks alive on a
projector and quietly exercises every layer underneath. Ours is seven apps
on a vSphere Kubernetes Service (VKS) cluster that VCF Automation
provisioned, in a tenant VPC, each behind its own load-balancer VIP.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">kubedoom</span>       <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.20</span><span class="p">:</span><span class="mi">5900</span>   <span class="p">(</span><span class="n">VNC</span> <span class="err">—</span> <span class="n">yes</span><span class="p">,</span> <span class="n">it</span> <span class="n">kills</span> <span class="n">pods</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">kubeinvaders</span>   <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.21</span>
</span></span><span class="line"><span class="cl"><span class="n">kube</span><span class="o">-</span><span class="n">ops</span><span class="o">-</span><span class="n">view</span>  <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.22</span>
</span></span><span class="line"><span class="cl"><span class="n">pacman</span>         <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.23</span>        <span class="p">(</span><span class="o">+</span> <span class="n">MongoDB</span> <span class="n">on</span> <span class="n">a</span> <span class="n">PVC</span> <span class="err">—</span> <span class="n">persistent</span> <span class="n">high</span> <span class="n">scores</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">podinfo</span>        <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.24</span>
</span></span><span class="line"><span class="cl"><span class="n">goldpinger</span>     <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.25</span>        <span class="p">(</span><span class="n">DaemonSet</span> <span class="n">incl</span><span class="o">.</span> <span class="n">control</span> <span class="n">plane</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">prometheus</span>     <span class="p">(</span><span class="ow">in</span><span class="o">-</span><span class="n">cluster</span><span class="p">:</span> <span class="n">KSM</span> <span class="o">+</span> <span class="n">node</span><span class="o">-</span><span class="n">exporter</span><span class="p">,</span> <span class="mi">11</span><span class="o">/</span><span class="mi">11</span> <span class="n">targets</span> <span class="n">up</span><span class="p">)</span>
</span></span></code></pre></div><p><img alt="Three of the seven live on their VIPs — KubeInvaders, kube-ops-view, Pac-Man — and the whole set as VCF Operations sees it" loading="lazy" src="/images/demo-apps-grid.jpg">
<em>Captured from the VIPs the platform handed out. KubeDoom is VNC-only and podinfo is an API, so they sit this one out; the Ops topology tile shows all seven by name.</em></p>
<h2 id="the-path-that-matters-tenanted-not-shortcut">The path that matters: tenanted, not shortcut</h2>
<p>I deployed the first version of this stack <em>against the supervisor</em>: an
admin kubeconfig, a vSphere namespace, <code>kubectl apply</code>. It worked, and it
was wrong, for the reason the <a href="/posts/vks-kubectl-vs-vcfa-all-apps/">previous
post</a> spells out. Nothing about it
was <em>provided</em> to anyone.</p>
<p>So I tore it down: seven apps, cluster, VPC and VIPs, gone in about seven
minutes. Demolition, as ever, was the quick part. Then I rebuilt it the
proper way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">dev-01 org → default-project → SupervisorNamespace (class large, region f06, VPC default-f06)
</span></span><span class="line"><span class="cl">          → VKS cluster vks-demo01 → seven apps
</span></span></code></pre></div><p>Everything went in <strong>through VCF Automation</strong>: the Cloud Consumption
Interface (CCI) API for the namespace and cluster, then the apps through
the cluster&rsquo;s own kubeconfig. Two auth facts are worth writing down; each
cost me a cycle:</p>
<ul>
<li>A <strong>provider</strong> service account reaches the cloud API only. CCI
(<code>/cci/kubernetes/apis/...</code>) rejects provider tokens with a 401. It needs an
<strong>org-scoped</strong> service account.</li>
<li>Device-flow login uses the service account&rsquo;s own UUID as the <code>client_id</code>
(not its software ID), against the tenant endpoint
<code>/oauth/tenant/&lt;org&gt;/device_authorization</code>.</li>
</ul>
<p><img alt="VCF Operations topology: the cluster with its apps named" loading="lazy" src="/images/ui/u9-ops-vks-topology.jpg"></p>
<h2 id="what-the-platform-does-for-free">What the platform does for free</h2>
<p>Each app is an ordinary Deployment plus a <code>Service</code> of type <code>LoadBalancer</code>.
The supervisor turns each service into an NSX VPC load-balancer virtual
server, and hands back a VIP from the org&rsquo;s external block. No ingress
controller, no MetalLB, no port-forwarding. Seven services, seven VIPs, done.</p>
<p>Pac-Man&rsquo;s MongoDB asks for a persistent volume and gets one on vSAN,
through the CSI driver the supervisor already installed. The high scores
are on enterprise storage. Priorities.</p>
<p>Goldpinger runs as a DaemonSet on every node, the control plane included,
and draws the node-to-node mesh live.</p>
<p>That&rsquo;s three platform services (load balancing, storage and networking)
exercised by apps that know nothing about VCF.</p>
<h2 id="the-traps">The traps</h2>
<p><strong>The supervisor refuses cluster-scoped RBAC, even to admin.</strong> Demo apps
that need ClusterRoles (kube-ops-view, Goldpinger, KubeDoom) <em>must</em> live on
a guest cluster. You can&rsquo;t run them as vSphere Pods on the supervisor.</p>
<p><strong>PodSecurity <code>restricted</code> is the VKS 1.35 default.</strong> Half the demo set
runs as root. The symptom: the Deployment shows <code>0 UP-TO-DATE</code>, the
ReplicaSet exists, zero pods, and the events say <code>FailedCreate</code>. The fix:
label the namespace <code>pod-security.kubernetes.io/enforce=privileged</code>. Then
say so in the demo, because it&rsquo;s a teaching moment.</p>
<p><strong>node-exporter without <code>hostNetwork</code>.</strong> The VPC fabric blocks scrapes from
a pod to a node IP, so the stock DaemonSet&rsquo;s <code>hostNetwork: true</code> doesn&rsquo;t
help. Run it as a normal pod and let Prometheus scrape it in-cluster.</p>
<p><strong>Docker Hub is flaky from behind a proxy.</strong> TLS handshake timeouts put
pods into kubelet&rsquo;s image-pull backoff, where they sit and sulk. Deleting
the stuck pods gets round the backoff. A Harbor proxy-cache project fixes
it properly.</p>
<p><strong>Pod CIDR shadowing.</strong> The stock <code>192.168.0.0/16</code> pod range hid the
org&rsquo;s <code>192.168.144.0/21</code> external block <em>from inside the cluster</em>, so apps
couldn&rsquo;t reach their neighbours&rsquo; VIPs. The pod CIDR is now <code>172.16.0.0/16</code>.</p>
<h2 id="automation-notes">Automation notes</h2>
<p>The whole stack is one checkbox on the lab&rsquo;s catalog item that deploys a
supervisor. <code>installDemoApps</code> creates the cluster, with the newest
compatible Kubernetes release and the newest built-in ClusterClass, both
auto-detected. Then it applies the seven apps and reports their URLs in the
deployment summary.</p>
<p>On a fresh environment, an integrated run takes 16.7 minutes to
<code>CREATE_SUCCESSFUL</code>. An immediate re-run takes 3.3 minutes, every step
idempotent. That&rsquo;s the number I actually care about: it makes a broken demo
a re-run, not a rebuild. Demos have an uncanny sense of when they&rsquo;re being
watched.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>A demo stack sounds like a toy. To be fair, one of the apps is Pac-Man.
But it&rsquo;s actually the fastest way to make a platform <em>legible</em> to people who
don&rsquo;t read YAML. A customer watches a request become a cluster, watches
seven services get their own addresses, then opens one and plays it.</p>
<p>Everything underneath gets exercised (self-service Kubernetes, load
balancing, persistent storage, isolation), and a non-technical stakeholder
can see it working. The same stack is what we put in front of a new team on
day one. And the same idempotent deploy is what makes it safe to
demonstrate live: if it breaks on stage, it re-runs in three minutes.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Demo apps that need cluster-scoped RBAC <strong>must</strong> run on a guest cluster.
The supervisor won&rsquo;t grant it, even to admin.</li>
<li>Build the stack through the <strong>tenanted path</strong>: org service account, then
CCI, then namespace, then cluster, then apps. Same apps, but now they&rsquo;re
provided, quota&rsquo;d and visible in Ops.</li>
<li>VKS 1.35 has <code>restricted</code> PodSecurity by default. Label the namespace and
say why.</li>
<li>One <code>LoadBalancer</code> per app means one NSX VIP per app. No ingress needed
for a demo.</li>
<li>Pick a pod CIDR that doesn&rsquo;t overlap the VPC external block.</li>
<li>Make the deploy idempotent. A demo that re-runs in 3 minutes is one you
can afford to break on stage.</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/administration-sdks-cli-and-tools/about-the-vcf-automation-api/tenant-portal/creating-and-managing-namespaces.html">Creating and Managing Namespaces</a>: a <code>SupervisorNamespace</code> with a class, region and VPC, through the All Apps API</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/administering-users-and-groups-in-vcf-automation-for-all-apps/create-a-service-account-in-your-vcf-automation-organization.html">Create a Service Account in Your VCF Automation Organization</a>: organization service accounts and their device-bound API tokens</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/deploying-workloads-on-tkg-service-clusters/pod-deployment-with-load-balancer-service.html">Pod Deployment with Load Balancer Service</a>: a <code>Service</code> of type <code>LoadBalancer</code> on a VKS cluster and its external IP</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/deploying-workloads-on-tkg-service-clusters/storage-concepts-for-tkg-service-clusters.html">Storage for VKS Clusters</a>: persistent volumes from the storage classes assigned to the namespace</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/managing-security-for-tkg-service-clusters/configure-psa-for-tkr-1-25-and-later.html">Configure PSA for VKr 1.25 and Later</a>: <code>restricted</code> enforced by default from VKr 1.26, and the namespace label that relaxes it</li>
</ul>
<p><em>Previously: <a href="/posts/vks-kubectl-vs-vcfa-all-apps/">one VKS cluster, two ways</a>.
The Pac-Man instance here is the one from <a href="/posts/whats-a-vpc-with-pacman/">What&rsquo;s a VPC?</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own.</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>One VKS cluster, two ways: kubectl vs VCF Automation All Apps</title>
      <link>https://thenestedlab.com/posts/vks-kubectl-vs-vcfa-all-apps/</link>
      <pubDate>Wed, 16 Sep 2026 06:50:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/vks-kubectl-vs-vcfa-all-apps/</guid>
      <description>The same VKS cluster built twice: once with kubectl, once as a VCF Automation request. The Cluster object is identical. Everything around it isn&amp;rsquo;t, and the difference is what you get for free.</description>
      <content:encoded><![CDATA[<p>The <code>Cluster</code> manifest is the same. That&rsquo;s the point of this post, and
also the punchline, so I&rsquo;ve rather given away the ending. <strong>VKS is VKS</strong>
whichever door you walk through.</p>
<p>What differs is everything wrapped around the cluster: who can ask for it,
what limits it, who can see it, and how it shows up in operations tooling.</p>
<p>So: two clusters, one supervisor, one ClusterClass, two paths.</p>
<h2 id="path-a-kubectl-the-way-weve-always-done-it">Path A: kubectl, the way we&rsquo;ve always done it</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">kubectl vsphere login --server &lt;supervisor&gt; --tanzu-kubernetes-cluster-namespace demo
</span></span><span class="line"><span class="cl">kubectl apply -f cluster.yaml
</span></span></code></pre></div><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">cluster.x-k8s.io/v1beta1</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">Cluster</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">clusterNetwork</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="l">pods, services, serviceDomain }  </span><span class="w"> </span><span class="c"># set all three — see below</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 class="c"># the gotcha</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</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 class="w"> </span><span class="l">one node pool, 2 replicas }</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 class="p">[</span><span class="w"> </span><span class="l">vmClass, storageClass ]</span><span class="w">
</span></span></span></code></pre></div><p>Fifteen minutes later: a cluster.</p>
<p>It does need a vSphere namespace first, and somebody (an admin) makes that
by hand in the vSphere Client. They attach a content library, assign a VM
class and set the quota. In this lab, that somebody is me.</p>
<h2 id="path-b-the-same-manifest-as-a-catalog-request">Path B: the same manifest, as a catalog request</h2>
<p>In All Apps, the cluster is a <code>CCI.Supervisor.Resource</code> inside a <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">blueprint</a>,
sitting next to a <code>CCI.Supervisor.Namespace</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">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">generateName</span><span class="p">:</span><span class="w"> </span><span class="l">${input.name}-</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 class="c"># quota comes from the class</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.vpc}</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 class="p">[</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><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cluster</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 class="l">&lt;the SAME Cluster object as above&gt;</span><span class="w">
</span></span></span></code></pre></div><p>Publish it, and a tenant user requests it from a tile:</p>
<p><img alt="VCFA: VKS cluster list as the tenant sees it" loading="lazy" src="/images/ui/u6a-vks-cluster-list.jpg">
<img alt="VCFA: cluster detail" loading="lazy" src="/images/ui/u6-vks-cluster-detail.jpg"></p>
<p>Fifteen minutes later: a cluster. Byte-identical <code>Cluster</code> object.</p>
<h2 id="what-path-b-adds-for-free">What path B adds for free</h2>
<table>
	<thead>
			<tr>
					<th></th>
					<th>kubectl</th>
					<th>All Apps request</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Who can create</td>
					<td>anyone with a kubeconfig to that namespace</td>
					<td>org users with catalog entitlement (org RBAC)</td>
			</tr>
			<tr>
					<td>Namespace</td>
					<td>pre-created by an admin, by hand</td>
					<td>created by the request, from a <strong>class</strong></td>
			</tr>
			<tr>
					<td>Quota</td>
					<td>set per namespace in the vSphere Client</td>
					<td>inherited from the namespace class (<code>small</code>/<code>medium</code>/<code>large</code>)</td>
			</tr>
			<tr>
					<td>Content library</td>
					<td>admin attaches manually</td>
					<td><code>contentSources</code> on the blueprint</td>
			</tr>
			<tr>
					<td>Networking</td>
					<td>whatever the namespace has</td>
					<td>pinned to a tenant VPC by <code>vpcName</code></td>
			</tr>
			<tr>
					<td>Sizing choices</td>
					<td>edit YAML</td>
					<td>form inputs with enums (worker count, VM class)</td>
			</tr>
			<tr>
					<td>Record</td>
					<td><code>kubectl get cluster</code></td>
					<td>a <strong>deployment</strong> with inputs, owner, history, day-2 actions</td>
			</tr>
			<tr>
					<td>Visibility</td>
					<td>supervisor only</td>
					<td>VCFA inventory <strong>and</strong> VCF Operations</td>
			</tr>
	</tbody>
</table>
<p>That last row is the one operations teams care about:</p>
<p><img alt="VCF Operations: the VKS cluster object with gauges and time series" loading="lazy" src="/images/ui/u8-ops-vks-summary.jpg">
<img alt="VCF Operations: topology view — the cluster and the apps on it, by name" loading="lazy" src="/images/ui/u9-ops-vks-topology.jpg"></p>
<p>The VCFA-deployed cluster appears in the Ops object model: supervisor, then
namespace, then cluster, then nodes, then the workloads running on it. Its
demo apps show up by name in the topology tab.</p>
<p>The kubectl-built cluster is <em>also</em> visible to Ops (it&rsquo;s the same
supervisor, after all). But it has no deployment, no owner, no request
history and no quota lineage. It&rsquo;s a thing that exists, not a thing that
was <em>provided</em>. As far as the paperwork goes, it simply turned up one day.</p>
<h2 id="the-four-gotchas-in-order-of-how-much-time-they-cost">The four gotchas, in order of how much time they cost</h2>
<p>Both paths share the same four traps on VKS 1.35 / VCF 9.1:</p>
<ol>
<li><strong>Our VCFA-created namespace had no content library.</strong> Zero
<code>VirtualMachineImage</code>s means no cluster. Set <code>contentSources</code> in the
blueprint, or attach one by hand. The 9.1 docs say a namespace class
<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">gets a content library automatically</a>; our <code>large</code> class had none
assigned.</li>
<li><strong><code>ClusterClass</code> lives in <code>vmware-system-vks-public</code>.</strong> It 404s from
the workload namespace unless the spec sets
<code>topology.classNamespace</code>.</li>
<li><strong>The default Quick Start namespace is 1000M CPU / 1000Mi.</strong> Unusable
for a cluster. Use a real class: <code>small</code> 10000M/10000Mi, <code>medium</code>
20000M, <code>large</code> 40000M.</li>
<li><strong>VKS 1.35 enforces PodSecurity <code>restricted</code> by default.</strong> Demo apps
that run as root get a ReplicaSet and <em>no pods</em>, and the events say
<code>FailedCreate</code>. Label the app namespace
<code>pod-security.kubernetes.io/enforce=privileged</code> (or fix the apps).</li>
</ol>
<p>Plus one that waits until later to bite, as the best ones do. Set
<code>clusterNetwork.serviceDomain</code> explicitly. It&rsquo;s immutable after create, and
without it some add-ons build the service DNS name wrongly (<code>....svc.</code> with
no domain).</p>
<p>And pick a pod CIDR that doesn&rsquo;t shadow your VPC&rsquo;s external range. The
stock <code>192.168.0.0/16</code> hid the org&rsquo;s <code>192.168.144.0/21</code> from inside the
cluster.</p>
<h2 id="so-which-one">So which one?</h2>
<p>Use <strong>kubectl</strong> when you&rsquo;re the platform team proving something on a
supervisor, or debugging.</p>
<p>Use <strong>All Apps</strong> the moment a second person needs a cluster. The request
form is the interface, and the namespace class is the guardrail. The
deployment is the audit trail, and Ops sees a provided service rather than
a stray object.</p>
<p>The cluster&rsquo;s the same either way. The <em>service</em> isn&rsquo;t.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>The business case for the second path is governance without friction.
Development teams get Kubernetes clusters on request. The platform team
gets quotas, ownership, RBAC and a monitoring view of every cluster, for
free.</p>
<p>That&rsquo;s the difference between a managed Kubernetes <em>service</em> and a
collection of clusters nobody can account for. It&rsquo;s typically the gap that
stops organisations offering Kubernetes broadly at all. Everything the
developers touch stays standard Kubernetes; the control lands around it,
not on it.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>The <code>Cluster</code> object is identical across paths. All Apps wraps it; it
doesn&rsquo;t change it.</li>
<li>What All Apps adds: catalog RBAC, class-based quota, library attach,
VPC pinning, deployment history, and a place in the Ops object model.</li>
<li>Four traps on 1.35: no library on our namespace class, <code>classNamespace</code>,
tiny default quota, PodSecurity <code>restricted</code>.</li>
<li>Set <code>serviceDomain</code> and a non-shadowing pod CIDR at create time. Both
are immutable.</li>
</ul>
<h2 id="broadcom-documentation">Broadcom documentation</h2>
<ul>
<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/workflow-for-provisioning-tkg-clusters-using-kubectl.html">Workflow for Provisioning VKS Clusters Using kubectl</a>: path A, from Supervisor login to an applied cluster YAML</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 built-in ClusterClass in <code>vmware-system-vks-public</code> and <code>spec.topology.classNamespace</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/sample-blueprints-in-vcf-automation-for-all-apps.html">Sample Blueprints in VCF Automation</a>: a <code>Cluster</code> as a <code>CCI.Supervisor.Resource</code> beside a <code>CCI.Supervisor.Namespace</code></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>: CPU and memory limits, VM classes, storage and content libraries per class, and the default small, medium and large</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/managing-security-for-tkg-service-clusters/configure-psa-for-tkr-1-25-and-later.html">Configure PSA for VKr 1.25 and Later</a>: <code>restricted</code> enforced by default from VKr 1.26, and the namespace label that relaxes it</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-consumption/latest/managing-vsphere-kuberenetes-service-clusters-and-workloads/operating-tkg-service-clusters/monitoring-vks-clusters-using-vcf-operations.html">Monitoring VKS Clusters Using VCF Operations</a>: VKS clusters in VCF Operations, monitored by default on VCF 9.1</li>
</ul>
<p><em>Next in All Apps in Practice: <a href="/series/all-apps-in-practice/">the demo apps that live on this
cluster</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
