<?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>Content-Libraries on The Nested Lab</title>
    <link>https://thenestedlab.com/tags/content-libraries/</link>
    <description>Recent content in Content-Libraries on The Nested Lab</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Tue, 06 Oct 2026 11:00:00 +0100</lastBuildDate>
    <atom:link href="https://thenestedlab.com/tags/content-libraries/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Bring your own image: a step done by hand in Org Builder</title>
      <link>https://thenestedlab.com/posts/org-builder-image-step/</link>
      <pubDate>Tue, 06 Oct 2026 11:00:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/org-builder-image-step/</guid>
      <description>Org Builder&amp;rsquo;s second worked example, the bring-your-own-image case: a team catalog that boots from an image, packaged without the image file, edited to ask for each site&amp;rsquo;s own copy at the right moment, and installed in production. Plus what VCF Automation&amp;rsquo;s own OVA export does with the same blueprint.</description>
      <content:encoded><![CDATA[<p><a href="/posts/org-builder-dev-to-prod/">Part 2</a> ended with a small confession: the
<strong>Linux VM</strong> item was in production&rsquo;s catalog, but the image it boots from
wasn&rsquo;t. This example puts that right. It&rsquo;s the bring-your-own-image case,
and it needs two things Org Builder hadn&rsquo;t been asked for yet: an image, and
a step only a person can do.</p>
<h2 id="the-image">The image</h2>
<p>demo-dev gets a content library, <code>team-images</code>, with the Ubuntu 24.04 server
cloud image in it. The blueprint doesn&rsquo;t change at all, because it already
reads the image&rsquo;s name from the <code>platformDefaults</code> property 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">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">imageName</span><span class="p">:</span><span class="w"> </span><span class="l">${propgroup.platformDefaults.linuxImage}</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">${propgroup.vmSizes[input.size]}</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">${propgroup.platformDefaults.storageClass}</span><span class="w">
</span></span></span></code></pre></div><p>A test request in development finished in 80 seconds, and the VM came up
with an address. VM Service turned the image&rsquo;s name into the library item&rsquo;s
own ID, so the blueprint never needs to know it. That matters in a moment, because the ID
is different in every organization.</p>
<h2 id="bring-your-own-image">Bring your own image</h2>
<p>The image takes 1.1 GB in the library. This example is the
bring-your-own-image case, on purpose: the package names the image, and each
site brings its own copy.</p>
<p>VCF Automation does offer the other road. Its <strong>Export</strong> dialog for a
blueprint has two formats: YAML, which is just the blueprint, and OVA, which
packs the blueprint together with the VM disk images it uses. So I asked it
what an OVA of <strong>Linux VM</strong> would carry.</p>
<p>No images at all. The export packs only the images a blueprint names itself,
and this one reads its image&rsquo;s name from a property group. I tried the
blueprint as it is, and three throwaway copies that name the same image in
other ways:</p>
<table>
	<thead>
			<tr>
					<th>How the blueprint names the image</th>
					<th>What the OVA export found</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>From the property group: <code>${propgroup.platformDefaults.linuxImage}</code></td>
					<td>no image: a 55 KB OVA</td>
			</tr>
			<tr>
					<td>From an input with a default: <code>${input.image}</code></td>
					<td>no image</td>
			</tr>
			<tr>
					<td>The name, written in: <code>ubuntu-24.04-server-cloudimg-amd64</code></td>
					<td>the image: a 566 MB OVA with its disk</td>
			</tr>
			<tr>
					<td>The image&rsquo;s ID, written in: <code>vmi-e24ba6f524c05ac13</code></td>
					<td>the image</td>
			</tr>
	</tbody>
</table>
<p>Naming the image in the export request itself changed nothing. The
property-group blueprint still came out at 55 KB: a blueprint, a manifest,
and no Ubuntu whatsoever. And what an OVA does carry is one blueprint (with
its custom form, when it has one), not the property groups it reads or its
catalog item.</p>
<p>So VCF Automation&rsquo;s OVA road means writing the image into the blueprint, and
with it one site&rsquo;s image into every site&rsquo;s copy. An image&rsquo;s ID also differs
in every organization; the end of this post shows two. Org Builder keeps the
image out of the blueprint, and out of the bundle: it exports the image by
name and size, never the file.</p>
<p><img alt="Export: the Images group with ubuntu-24.04-server-cloudimg-amd64 ticked, in library team-images, an OVF of 1.1 GB" loading="lazy" src="/images/image-step-export-image.jpg">
<em>Only the catalog brings the image with it, by name. The library it lives in stays behind: production has its own.</em></p>
<p>The bundle says so in as many words: no image files, and the destination adds
each image to its own library by name.</p>
<p><img alt="Bundle written: team-catalog 1.1.0 with 2 blueprints, 2 catalog items, 1 image and 2 property groups, the image by name only, and buttons for Done, Install it in another organization and Edit it first" loading="lazy" src="/images/image-step-bundle.jpg">
<em>team-catalog 1.1.0: the image by name, no file.</em></p>
<h2 id="a-step-only-a-person-can-do">A step only a person can do</h2>
<p>Someone has to bring that image to the destination. The obvious place to say
so is a runbook. The trouble with runbooks is that the person installing the
package has to find them, read them, and remember them at the right moment.</p>
<p>So the package says it itself. <strong>Edit a package</strong> opens the bundle, and a
step done by hand goes in at its place in the install: just before the
image.</p>
<p><img alt="Steps done by hand: the title Bring the Ubuntu image, shown before the image ubuntu-24.04-server-cloudimg-amd64, and what the person does: upload the OVA into this site&rsquo;s image library, keeping its name, then answer Done" loading="lazy" src="/images/image-step-edit-step.jpg">
<em>The step, in the package&rsquo;s own words, placed before the image it&rsquo;s about.</em></p>
<p>It saves as a new version, 1.2.0, in a new folder. Version 1.1.0 stays exactly
as it was. That&rsquo;s not fussiness: Org Builder records which version each site
has, and a package that changed under the same number would confuse every
upgrade after it.</p>
<p><img alt="Save as a new version: version 1.2.0 in a new bundle folder, written, made from 1.1.0, with one step done by hand" loading="lazy" src="/images/image-step-edit-written.jpg">
<em>1.2.0, made from 1.1.0. The one it was made from is never changed.</em></p>
<h2 id="install-in-production">Install in production</h2>
<p>demo-prod already runs 1.0.0 from part 2, so this is an upgrade. Production
keeps its images in a library of its own, called <code>prod-images</code>, so it gives
that name instead of development&rsquo;s.</p>
<p><img alt="Settings for this site, Names at this site: the content library the images go into, prod-images here and team-images in demo-dev; the project, default-project in both; and the image, looked for by name in this site&rsquo;s library" loading="lazy" src="/images/image-step-names.jpg">
<em>Production names its own library. The project has the same name in both.</em></p>
<p>The preview reads production and changes nothing. One thing to create (the
image), one step done by hand, and eight already there: the six from 1.0.0,
the project and the library. It also says, before anything starts, that the
image isn&rsquo;t in the library yet.</p>
<p><img alt="Preview: 1 to create, 1 runbook step, 8 already there; ready to import; and a warning that the import cannot bring in the image ubuntu-24.04-server-cloudimg-amd64 and will ask for it" loading="lazy" src="/images/image-step-preview.jpg">
<em>The preview knows the image is missing, and says so before anything starts.</em></p>
<p>Then the import runs, and stops at the step. The question box shows the
step&rsquo;s own title and words, with three answers: Done, Skip or Stop here.</p>
<p><img alt="The import&rsquo;s panel: step done by hand in team-catalog, Bring the Ubuntu image, the package&rsquo;s instructions, and the buttons Done, Skip and Stop here" loading="lazy" src="/images/image-step-ask.jpg">
<em>The package&rsquo;s own words, at the moment they matter.</em></p>
<p>The image then went into <code>prod-images</code> under its own name. In our lab a
script did the upload while the import waited, in a little over two minutes,
and the film skips it. Then Done. The import found the image by name,
recorded its ID, and carried on.</p>
<p><img alt="Imported: 8 of 8 done, team-catalog finished after 4 minutes" loading="lazy" src="/images/image-step-imported.jpg">
<em>8 of 8 done. The step is recorded as done by hand, with the answer.</em></p>
<p>Here&rsquo;s the whole thing on the page: create the package, edit it, install it.</p>
<figure class="nl-video">
  <video autoplay loop muted playsinline controls preload="metadata" style="aspect-ratio:1440 / 992" poster="/images/org-builder-image-step-poster.jpg">
    <source src="/images/org-builder-image-step.mp4" type="video/mp4">
  </video>
  <figcaption>Create a package from demo-dev, edit it to add a step done by hand, then install it in demo-prod, where the install stops at the step. Silent, with captions.</figcaption>
</figure>

<h2 id="the-result-in-vcf-automation-itself">The result, in VCF Automation itself</h2>
<p>Production&rsquo;s library has the image, as VCF Automation reports it:</p>
<p><img alt="Content Libraries in demo-prod: prod-images, Ready, in region f06, an organization library" loading="lazy" src="/images/image-step-vcfa-library.jpg">
<em>prod-images, made in production before the import.</em></p>
<p><img alt="VM Images in demo-prod: ubuntu-24.04-server-cloudimg-amd64, Ready, with its own image identifier, in library prod-images" loading="lazy" src="/images/image-step-vcfa-images.jpg">
<em>The image, with production&rsquo;s own ID.</em></p>
<p>And a request for <strong>Linux VM</strong> in production makes a VM that boots from it:</p>
<p><img alt="Instances, Virtual Machines in demo-prod: prod-vm1, Ready, power on, IP address 172.30.0.2, in default-project, VM image vmi-c59f640093a4231ab, VM class best-effort-small" loading="lazy" src="/images/image-step-vcfa-vm.jpg">
<em>prod-vm1: on, with an address, booted from production&rsquo;s copy of the image.</em></p>
<p>Same image name in both organizations, two different IDs, and a blueprint
that never had to know either of them.</p>
<h2 id="what-the-example-found">What the example found</h2>
<p>Worked examples are useful for finding things, and this one obliged three
times.</p>
<ul>
<li><strong>Production&rsquo;s library in the wrong place.</strong> The first script made
development&rsquo;s library, then cheerfully made production&rsquo;s library in
development too. Python loads a module once, so the second organization&rsquo;s
sign-in quietly became the first one&rsquo;s. One organization per process fixed
it; the stray library was deleted.</li>
<li><strong>A library by the wrong name.</strong> The first preview looked for development&rsquo;s
library name in production. It now looks for the name this site gives,
which is what <strong>Names at this site</strong> is for.</li>
<li><strong>A Done that went nowhere.</strong> In the first take, the page&rsquo;s answer to the
step was refused, because an import&rsquo;s job had no build site file behind
it. The four-minute wait also lasted no time at all, which is quicker than
any upload I&rsquo;ve seen. Both are fixed; the film above is the second take.</li>
</ul>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>Images are where catalog moves tend to come unstuck. They&rsquo;re big, and each
site usually has its own, built, patched and approved by its own team. A
package that carried them would be slow to copy, and often wrong for the
place it lands.</p>
<p>Naming the image instead keeps the bundle small and leaves the choice of
copy with the site. The step done by hand puts the instruction where the
person installing will meet it, not in a document they may never open. And
because the preview checks the library first, a missing image turns up while
everyone is still planning, not when the first person requests a VM.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Bring your own image: name it in the package, and let each site bring its
own copy.</li>
<li>VCF Automation&rsquo;s OVA export carries only the images a blueprint names
itself. An image read from a property group or an input stays behind, even
when the export request names it.</li>
<li>When a package needs a person, put the step in the package, at the place it
matters, not in a runbook somebody has to remember to open.</li>
<li>Every change is a new version; the version you opened stays as it was.</li>
<li>Let each site name its own library and projects, and check them in the
preview before anything is made.</li>
</ul>
<h2 id="whats-next">What&rsquo;s next</h2>
<p>The next posts leave this small catalog behind for a bigger one: the nested
lab catalog, built and moved with the same packages.</p>
<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/import-or-export-a-stateful-blueprint-in-vcf-automation.html">Importing or Exporting Blueprints in VCF Automation</a>: a blueprint and its VM images as one bundle, and what an import accepts</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/import-or-export-a-stateful-blueprint-in-vcf-automation/export-a-blueprint-from-vcf-automation.html">Export a Blueprint from VCF Automation</a>: the Export dialog, versions, images and the Exports tab</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/setting-up-the-content-hub-in-vcf-automation-for-all-apps-organizations/adding-and-managing-content-libraries.html">Managing Content Libraries in VCF Automation</a>: provider, organization and project libraries, and which namespaces see them</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/setting-up-the-content-hub-in-vcf-automation-for-all-apps-organizations/adding-and-managing-content-libraries/create-a-content-library.html">Create an Organization Content Library in VCF Automation</a>: a library for every namespace in the organization</li>
<li><a href="https://techdocs.broadcom.com/us/en/vmware-cis/vcf/vcf-9-0-and-later/9-1/organization-management/setting-up-the-content-hub-in-vcf-automation-for-all-apps-organizations/adding-and-managing-content-libraries/creating-vm-images.html">Add a VM Image to a Content Library in VCF Automation</a>: uploading an OVA or OVF</li>
</ul>
<hr>
<p><em>Lab environment; opinions my own. Org Builder is our own tooling, not a
Broadcom product. VMware, VCF and VCF Automation are trademarks of Broadcom
Inc. or its subsidiaries.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
