<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <title>Michael Ryom (.dk)</title>
    <link href="https://MichaelRyom.dk/feed.xml" rel="self" />
    <link href="https://MichaelRyom.dk" />
    <updated>2026-04-25T09:22:58+02:00</updated>
    <author>
        <name>Michael Ryom</name>
    </author>
    <id>https://MichaelRyom.dk</id>

    <entry>
        <title>Import-DSvRepo - One Script to Import Everything</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/import-dsvrepo-one-script-to-import-everything/"/>
        <id>https://MichaelRyom.dk/import-dsvrepo-one-script-to-import-everything/</id>
            <category term="vmware"/>
            <category term="broadcom"/>
            <category term="automation"/>

        <updated>2026-04-25T08:51:41+02:00</updated>
            <summary type="html">
                <![CDATA[
                    Import-DSvRepo - One Script to Import Everything Available now This is the third piece of the air-gapped VMware patching puzzle. DSvClient downloads the repository from&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <h1 id="import-dsvrepo---one-script-to-import-everything">Import-DSvRepo - One Script to Import Everything</h1>
<p><a href="https://github.com/MichaelRyom/Import-DSvRepo"><strong>Available now</strong></a></p><p>This is the third piece of the air-gapped VMware patching puzzle. DSvClient downloads the repository from Broadcom, DSvRepoImport is the PowerShell module that knows how to talk to each vCenter API, and Import-DSvRepo is the wrapper script that ties them together into a single command.</p><p>If you’ve read my posts on <a href="/dsvclient-080-vcsa-patch-layout-fix-and-on-disk-cdn-mirror/">DSvClient 0.8.0</a> and <a href="/dsvrepoimport-importing-vmware-repos-into-air-gapped-vcenter/">DSvRepoImport</a>, you know the individual pieces. This post covers how the wrapper orchestrates them.</p><h3 id="why-a-wrapper-script">Why a Wrapper Script?</h3>
<p>The DSvRepoImport module exports eight functions, each handling a different data type. Calling them one by one with the right parameters and credentials gets tedious fast — especially when you’re doing it regularly across multiple vCenter instances.</p><p><code>Import-DSvRepo.ps1</code> reduces it to:</p><pre><code class="language-powershell">.\Import-DSvRepo.ps1 -VCenterServer vcsa.domain.local `
    -RepoPath D:\VMware-repo `
    -VersionFilter &#39;8.0*&#39; `
    -BuildFilter &#39;2*&#39;
</code></pre>
<p>Two credential prompts (one for the vCenter REST API, one for VCSA OS root), then it runs through everything automatically.</p><h3 id="what-it-does-in-order">What It Does, In Order</h3>
<p>The script calls the module functions in dependency order:</p><ol>
<li><strong><code>Import-VsanHclDatabase</code></strong> — uploads the vSAN Hardware Compatibility List JSON from <code>hcl/all.json</code> in the repo</li>
<li><strong><code>Import-VsanReleaseCatalog</code></strong> — uploads the vSAN Release Catalog from <code>vcg/vsan_release_catalog.json</code></li>
<li><strong><code>Import-VcgDatabase</code></strong> — uploads the VCG compatibility database from <code>vvs_data.gz</code></li>
<li><strong><code>Import-VlcmOfflineBundle</code></strong> — scans the repo for all four depot types (<code>main/</code>, <code>addon-main/</code>, <code>iovp-main/</code>, <code>vmtools-main/</code>) and imports each as vLCM offline depot bundles. Handles version/build filtering, size-based chunking for the 2 GB API limit, and deduplication of already-imported depots.</li>
</ol>
<p>VCSA appliance patching (<code>Install-VcsaPatch</code>) is intentionally <strong>not</strong> called by the wrapper. Patching is destructive — it restarts all vCenter services and can take 30+ minutes — so it should be a conscious decision, not something that runs as part of a bulk import.</p><h3 id="parameters">Parameters</h3>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>-VCenterServer</code></td>
<td>FQDN or IP of the vCenter instance</td>
</tr>
<tr>
<td><code>-RepoPath</code></td>
<td>Path to the DSvClient-downloaded repository root</td>
</tr>
<tr>
<td><code>-VCSAUser</code></td>
<td>Username for VCSA OS access (default: <code>root</code>)</td>
</tr>
<tr>
<td><code>-VersionFilter</code></td>
<td>Wildcard filter for ESXi/vCenter versions to import (e.g. <code>8.0*</code>)</td>
</tr>
<tr>
<td><code>-BuildFilter</code></td>
<td>Wildcard filter for build numbers</td>
</tr>
<tr>
<td><code>-CleanupBeforeImport</code></td>
<td>Remove all existing vLCM offline depots before importing. Off by default — use this when you want a clean slate.</td>
</tr>
</tbody></table>
<h3 id="version-and-build-filtering">Version and Build Filtering</h3>
<p>The <code>-VersionFilter</code> and <code>-BuildFilter</code> parameters are passed through to <code>Import-VlcmOfflineBundle</code> and control which ESXi builds get packaged into offline depot bundles. If your repo contains every ESXi 7.x and 8.x build from the last two years but you only care about 8.0 Update 3, filtering saves significant time and disk space on the VCSA:</p><pre><code class="language-powershell"># Only import ESXi 8.0 builds starting with &quot;24&quot; or &quot;25&quot; (U3 era)
.\Import-DSvRepo.ps1 -VCenterServer vcsa.domain.local `
    -RepoPath D:\VMware-repo `
    -VersionFilter &#39;8.0*&#39; `
    -BuildFilter &#39;2[45]*&#39;
</code></pre>
<p>Without filters, everything in the repo gets imported — which is fine if that’s what you want, just slower.</p><h3 id="the-typical-air-gapped-workflow">The Typical Air-Gapped Workflow</h3>
<p>Here’s how the three tools fit together end-to-end:</p><p><strong>On your internet-connected machine:</strong></p><pre><code class="language-bash"># 1. Download everything from Broadcom
./DSvClient --config sources.toml --output /mnt/usb/VMware-repo
</code></pre>
<p><strong>Transfer the repo to the air-gapped network</strong> (USB drive, data diode, sneakernet, whatever your security policy requires).</p><p><strong>On your management workstation inside the air gap:</strong></p><pre><code class="language-powershell"># 2. Import depot data into vCenter
.\Import-DSvRepo.ps1 -VCenterServer vcsa.domain.local `
    -RepoPath D:\VMware-repo

# 3. If there&#39;s a VCSA appliance patch to apply
Install-VcsaPatch -VCSAHost vcsa.domain.local -Credential $cred `
    -SourcePath &#39;D:\VMware-repo\valm\8.0.3.00800&#39; -Install
</code></pre>
<p>Step 2 takes 10-30 minutes depending on how many depot bundles need uploading (the SFTP transfer of multi-GB bundles to the appliance is the bottleneck). Step 3 takes another 20-40 minutes for the actual patch installation.</p><h3 id="prerequisites">Prerequisites</h3>
<p>The script requires the DSvRepoImport module and its dependencies:</p><pre><code class="language-powershell">Install-Module VMware.PowerCLI -Scope CurrentUser
Install-Module WinSCP -Scope CurrentUser
Install-Module Posh-SSH -Scope CurrentUser
</code></pre>
<p>Place <code>DSvRepoImport.psm1</code> and <code>DSvRepoImport.psd1</code> somewhere on your <code>$env:PSModulePath</code>, or <code>Import-Module</code> them explicitly before running the wrapper.</p><h3 id="whats-next">What’s Next</h3>
<p>For the next iteration I’m considering adding a <code>-WhatIf</code> mode that scans the repo and reports what <em>would</em> be imported without actually doing it — useful for reviewing the scope of an import before committing. Also on the list is support for generating a diff report between what’s already in vCenter’s vLCM depot and what’s in the local repo, so you can see exactly what’s new before importing.</p><p>Both the module and the wrapper script are on my OneDev instance. DSvClient is on <a href="https://github.com/MichaelRyom/DSvClient">GitHub</a>. Questions or issues — reach out. 🛠️</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>DSvRepoImport - Importing VMware Repos into Air-Gapped vCenter</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/dsvrepoimport-importing-vmware-repos-into-air-gapped-vcenter/"/>
        <id>https://MichaelRyom.dk/dsvrepoimport-importing-vmware-repos-into-air-gapped-vcenter/</id>
            <category term="vmware"/>
            <category term="broadcom"/>
            <category term="automation"/>

        <updated>2026-04-25T08:48:22+02:00</updated>
            <summary type="html">
                <![CDATA[
                    DSvRepoImport - Importing VMware Repos into Air-Gapped vCenter Available now If you run vCenter in an air-gapped environment — no internet access, no Broadcom CDN&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <h1 id="dsvrepoimport---importing-vmware-repos-into-air-gapped-vcenter">DSvRepoImport - Importing VMware Repos into Air-Gapped vCenter</h1>
<p><a href="https://github.com/MichaelRyom/DSvRepoImport"><strong>Available now</strong></a></p><p>If you run vCenter in an air-gapped environment — no internet access, no Broadcom CDN connectivity — getting vSAN HCL databases, VCG compatibility data, vLCM offline depot bundles, and VCSA appliance patches onto the appliance is a multi-step manual process that’s easy to get wrong.</p><p>I wrote DSvRepoImport to automate that entire pipeline. It’s a PowerShell module that takes a DSvClient-downloaded repository and imports everything into vCenter over SSH and REST API, with no internet connectivity required on the vCenter side.</p><h3 id="what-it-imports">What It Imports</h3>
<p>The module handles six distinct data types, each with its own import mechanism:</p><table>
<thead>
<tr>
<th>Function</th>
<th>What It Imports</th>
<th>API Used</th>
</tr>
</thead>
<tbody><tr>
<td><code>Import-VsanHclDatabase</code></td>
<td>vSAN Hardware Compatibility List</td>
<td>REST API upload</td>
</tr>
<tr>
<td><code>Import-VsanReleaseCatalog</code></td>
<td>vSAN Release Catalog</td>
<td>REST API upload</td>
</tr>
<tr>
<td><code>Import-VcgDatabase</code></td>
<td>VCG Compatibility Database</td>
<td>REST API upload</td>
</tr>
<tr>
<td><code>Import-VlcmOfflineBundle</code></td>
<td>vLCM offline depot bundles (ESXi base, OEM addons, IO drivers, VMware Tools)</td>
<td>REST API + SFTP + on-appliance HTTP server</td>
</tr>
<tr>
<td><code>New-VcsaPatchIso</code></td>
<td>Builds a VCSA patch ISO from unpacked <code>valm/</code> folder</td>
<td>Local IMAPI2FS COM objects</td>
</tr>
<tr>
<td><code>Install-VcsaPatch</code></td>
<td>Stages and installs VCSA appliance patches via loopback-mounted ISOs</td>
<td>SSH + SFTP</td>
</tr>
</tbody></table>
<h3 id="the-vlcm-bundle-challenge">The vLCM Bundle Challenge</h3>
<p>The vLCM offline depot import was the trickiest part. vCenter’s offline depot API only accepts <code>http(s)://</code> URLs — no <code>file:///</code> paths, no local directories. In an air-gapped environment, there’s nowhere to host an HTTP server that vCenter can reach.</p><p>The solution: SFTP the bundle to the VCSA, spin up a temporary Python HTTP server on the appliance itself, point the vLCM API at <code>http://localhost:8888/</code>, wait for the import task to complete, then tear down the server.</p><p>There’s also a 2 GB hard limit on individual bundles — vCenter’s download manager uses a signed 32-bit byte counter that overflows at 2 GB. The module enforces a 1800 MB ceiling and bin-packs VIBs into chunks for large flat-catalog depot types like <code>vmtools-main/</code>.</p><h3 id="vcsa-appliance-patching-without-vm-console-access">VCSA Appliance Patching Without VM Console Access</h3>
<p>The <code>Install-VcsaPatch</code> function is designed for environments where you have SSH access to the VCSA but not hypervisor-level access to attach a virtual CD-ROM. The VCSA’s <code>software-packages</code> command only supports <code>--iso</code> and <code>--url</code> for patch media — it expects the patch contents mounted at <code>/mnt/iso-contents</code>.</p><p>The module replicates that mount point with a loopback mount:</p><ol>
<li>Build a flat ISO from the <code>valm/&lt;version&gt;/</code> folder using Windows IMAPI2FS (no external tools needed)</li>
<li>SFTP the ~8 GB ISO to <code>/storage/core/</code> on the VCSA</li>
<li><code>losetup</code> + <code>/dev/cdromN</code> symlink so the VCSA’s <code>mountISO()</code> function finds it</li>
<li>Run <code>software-packages stage --iso --acceptEulas</code> then <code>software-packages install --staged</code></li>
<li>Clean up loop device, symlink, and ISO file</li>
</ol>
<p>Two modes:</p><pre><code class="language-powershell"># Validate a patch without installing (stage only)
Install-VcsaPatch -VCSAHost vcsa.domain.local -Credential $cred `
    -SourcePath D:\VMware-repo\valm\8.0.3.00800 -Stage

# Stage + install in one shot
Install-VcsaPatch -VCSAHost vcsa.domain.local -Credential $cred `
    -SourcePath D:\VMware-repo\valm\8.0.3.00800 -Install
</code></pre>
<h3 id="a-key-discovery-vcsa-patch-isos-are-flat">A Key Discovery: VCSA Patch ISOs Are Flat</h3>
<p>One thing I learned the hard way: a VCSA patch ISO must be <strong>completely flat</strong> — every file at the mount root, no <code>package-pool/</code> subdirectory. Even though <code>manifest-latest.xml</code> references RPMs as <code>&lt;location&gt;package-pool/foo.rpm&lt;/location&gt;</code>, the VCSA’s stager code (<code>functions_target.py::parseRpmXmlManifestToJson</code>) calls <code>os.path.basename()</code> to strip that prefix before looking up files on the ISO.</p><p>The <code>&lt;location&gt;</code> prefix only matters in online-URL mode (<code>software-packages stage --url</code>), where it becomes part of the HTTP request path. For ISO staging it’s discarded. I spent quite a few iterations figuring this out by reverse-engineering the stager Python code from inside <code>target-patch-scripts.zip</code>.</p><h3 id="pre-flight-and-post-install-checks">Pre-Flight and Post-Install Checks</h3>
<p>After hitting several avoidable issues during patching (stale loop devices from prior failed runs, stuck <code>INSTALL_FAILED</code> state in the appliance’s CLI wrapper, duplicate authz roles breaking a post-install hook), I added automated checks:</p><pre><code class="language-powershell"># Pre-flight readiness check
Test-VcsaPatchReadiness -VCSAHost vcsa.domain.local -Credential $cred |
    Format-Table Name, Status, Detail -AutoSize

# Post-install verification
Test-VcsaPatchInstalled -VCSAHost vcsa.domain.local -Credential $cred `
    -ExpectedVersion &#39;8.0.3.00800&#39; -ExpectedBuild &#39;25197330&#39; |
    Format-Table Name, Status, Detail -AutoSize
</code></pre>
<p><code>Test-VcsaPatchReadiness</code> checks SSH connectivity, core services, disk space on all storage partitions, vCHA state, hostname/PNID match, stuck failed-install state, and orphaned loop devices. <code>Test-VcsaPatchInstalled</code> verifies the installed version/build, services, residual state, and HTTPS endpoint.</p><p>Both return structured <code>Name / Status / Detail / Remediation</code> records so you can script around them.</p><h3 id="the-cleanup-landmine">The Cleanup Landmine</h3>
<p>One pattern that bit me repeatedly: when a <code>-Stage</code> or <code>-Install</code> run fails mid-flight, the loopback device and <code>/dev/cdromN</code> symlink are deliberately left in place so you can investigate. But the next run creates a <em>new</em> loop device at a different <code>/dev/cdromN</code> slot, and the VCSA’s <code>mountISO()</code> iterates <code>/dev/cdrom*</code> alphabetically — picking up the stale one first.</p><p>The module now auto-cleans residual loop devices and cdrom symlinks at the start of every run, and verifies each cleanup command’s exit status individually at the end. No more silent cleanup failures that poison the next attempt.</p><h3 id="dependencies">Dependencies</h3>
<pre><code class="language-powershell">Install-Module VCF.PowerCLI -Scope CurrentUser #Real Requirements: VMware.VimAutomation.Core, VMware.VimAutomation.Cis.Core, VMware.VimAutomation.Storage
Install-Module WinSCP -Scope CurrentUser
Install-Module Posh-SSH -Scope CurrentUser
</code></pre>
<p>The module requires PowerShell 5.1+, VMware/VCF PowerCLI (for vSAN/VCG/vLCM REST API access), WinSCP (for SFTP with the custom <code>SftpServer</code> raw setting that VCSA needs), and Posh-SSH (for SSH command execution).</p><h3 id="getting-started">Getting Started</h3>
<p>The companion wrapper script <code>Import-DSvRepo.ps1</code> calls all the import functions in sequence:</p><pre><code class="language-powershell">.\Import-DSvRepo.ps1 -VCenterServer vcsa.domain.local `
    -RepoPath D:\VMware-repo `
    -VersionFilter &#39;8.0*&#39; `
    -BuildFilter &#39;2*&#39;
</code></pre>
<p>Both repos are available on my OneDev instance. DSvClient (the downloader that produces the repo) is on <a href="https://github.com/MichaelRyom/DSvClient">GitHub</a>. 🔧</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>DSvClient 0.8.0 - VCSA Patch Layout Fix and On-Disk CDN Mirror</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/dsvclient-080-vcsa-patch-layout-fix-and-on-disk-cdn-mirror/"/>
        <id>https://MichaelRyom.dk/dsvclient-080-vcsa-patch-layout-fix-and-on-disk-cdn-mirror/</id>
            <category term="vmware"/>
            <category term="broadcom"/>

        <updated>2026-04-13T15:10:48+02:00</updated>
            <summary type="html">
                <![CDATA[
                    A few critical bugs have been fixed in the DSvClient to make it work when patching vCenter, some blobs where missing and layout didnt align&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p>A few critical bugs have been fixed in the DSvClient to make it work when patching vCenter, some blobs where missing and layout didnt align with vCenters expectations when used for an online repo. This make it ready to use with the up coming patch module for vCenter, very useful in airgapped environments.<br><br>Here's what changed and why.<br><br></p>
<h1>Why This Release Exists</h1>
<p>Previous DSvClient versions downloaded every VCSA patch file — RPMs, container image blobs, `.manifest` files, script zips, and root metadata — into a single flat directory at `valm/&lt;version&gt;/`. That worked for local inspection, but it didn't match how the Broadcom CDN actually serves those files.<br><br>On the CDN, the layout looks like this:<br><br><br><code>valm/8.0.3.00800/</code><br><code>    manifest-latest.xml</code><br><code>    manifest-latest.xml.sha256</code><br><code>    manifest-latest.xml.sign</code><br><code>    rpm-manifest.json</code><br><code>    rpm-manifest.json.sha256</code><br><code>    rpm-manifest.json.sign</code><br><code>    package-pool/</code><br><code>        *.rpm</code><br><code>        *.blob</code><br><code>        *.manifest</code><br><code>        patch-metadata-scripts.zip</code><br><code>        target-patch-scripts.zip</code><br><br><br>Only the six metadata files live at the root. Everything else — the hundreds of RPMs, the container image blobs, the `.manifest` container descriptors, and the two `*-patch-scripts.zip` script bundles — lives under `package-pool/`.<br><br></p>
<h1>What Changed in 0.8.0</h1>
<p>The `get_vcsa_file_path` function in `downloader.rs` now preserves the `package-pool/` prefix from each package's `&lt;location&gt;` or `relativepath` entry, instead of stripping it to the basename:<br><br></p>
<pre class="language-rust"><code>fn get_vcsa_file_path(&amp;self, version: &amp;str, file: &amp;str) -&gt; PathBuf {
    let mut path = self.base_path.join("valm").join(version);
    for segment in file.split('/')
        .filter(|s| !s.is_empty() &amp;&amp; *s != ".." &amp;&amp; *s != ".")
    {
        path = path.join(segment);
    }
    path
}</code></pre>
<p><br>The `..` and `.` segments are stripped defensively so a hostile manifest can't write outside the version directory. The `download_file` function already creates missing parent directories via `create_dir_all`, so the `package-pool/` subdirectory is created on demand.<br><br></p>
<h1>Why This Matters</h1>
<p>With the on-disk layout now matching the CDN, a freshly-downloaded `valm/&lt;version&gt;/` tree can be:<br><br>1. **Served directly over HTTP** as a local mirror of the Broadcom CDN for `software-packages stage --url http://host/valm/&lt;version&gt;/`<br>2. **Turned into a VCSA patch ISO** via the <a href="https://michaelryom.dk/dsvrepoimport-importing-vmware-repos-into-air-gapped-vcenter/" target="_blank" rel="noopener noreferrer">DSvRepoImport PowerShell module</a><br>3. **Inspected locally** the same way as before — only the directory structure changed<br><br></p>
<h1>Migrating an Existing Flat Repo</h1>
<p>If you already downloaded a repo with DSvClient 0.6 or earlier and don't want to re-download ~8 GB, a quick shell one-liner reshuffles the files:<br><br></p>
<pre class="language-bash"><code>cd /path/to/VMware-repo/valm/8.0.3.00800
mkdir -p package-pool
for f in *; do
  case "$f" in
    package-pool|manifest-latest.xml|manifest-latest.xml.sha256|\
    manifest-latest.xml.sign|rpm-manifest.json|\
    rpm-manifest.json.sha256|rpm-manifest.json.sign) ;;
    *) mv -- "$f" package-pool/ ;;
  esac
done</code></pre>
<p>PowerShell equivalent:</p>
<pre class="language-powershell"><code>$src = 'C:\path\to\VMware-repo\valm\8.0.3.00800'
$keep = @('manifest-latest.xml','manifest-latest.xml.sha256',
          'manifest-latest.xml.sign','rpm-manifest.json',
          'rpm-manifest.json.sha256','rpm-manifest.json.sign')
New-Item -ItemType Directory -Path (Join-Path $src 'package-pool') -Force | Out-Null
Get-ChildItem -Path $src -File |
  Where-Object { $keep -notcontains $_.Name } |
  Move-Item -Destination (Join-Path $src 'package-pool')</code></pre>
<p>After migration, running DSvClient 0.8.0 again will verify all checksums and skip already-valid files — no re-download.<br><br></p>
<h1>Also in This Release</h1>
<p>- **Version 0.7.0** (bundled in this release) added `parse_vcsa_rpm_manifest_json` to download `.blob` and `.manifest` files referenced by `rpm-manifest.json` that weren't previously being fetched. Without these 24+ container image blobs and 2 container manifests, VCSA patching fails silently during the staging phase.<br><br>DSvClient is open source and available on <a href="https://github.com/MichaelRyom/DSvClient" target="_blank" rel="noopener noreferrer">GitHub</a>. <br>Grab the latest release binary or build from source with `cargo build --release`. 🚀</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Capturing VM Switch‑Port Traffic with a simple script</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/capturing-vm-switch-port-traffic-with-a-simple-script/"/>
        <id>https://MichaelRyom.dk/capturing-vm-switch-port-traffic-with-a-simple-script/</id>

        <updated>2025-09-16T04:17:58+02:00</updated>
            <summary type="html">
                <![CDATA[
                    When I’m troubleshooting a virtual machine’s network behavior, the first thing I reach for is a packet capture. In theory it’s simple: locate the switch‑port&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p>When I’m troubleshooting a virtual machine’s network behavior, the first thing I reach for is a packet capture. In theory it’s simple: locate the switch‑port the VM is attached to, start a capture on that port, and save the results for later analysis. In practice I kept forgetting the exact commands—especially the quirky combination of <code>pktcap-uw</code> and <code>tcpdump-uw</code> that ESXi require.</p>
<p>To solve that, I wrote a tiny shell script that does everything in one go. Below I walk through the script, explain each step, so you can use it in your own environment.</p>
<p> </p>
<p>The script can be found <a href="https://codeberg.org/MichaelRyom/VMware_Bash_Scripts/src/branch/main/Capture_VM_Switchport_Traffic.sh" target="_blank" rel="noopener">here over on Codeberg.org</a></p>
<h3> </h3>
<h3>Why This Script Exists</h3>
<ul>
<li><strong>Forgetfulness is real.</strong> Every time I needed a capture I’d open a terminal, remember the command <code>net‑stats -l</code> or using <code>esxcli</code> and whatever parameters is needed, extract the port number, remember the right flags for <code>pktcap-uw</code>, pipe it into <code>tcpdump-uw</code>, and finally name the output file and forget to give it a new one when doing multiple takes.</li>
<li><strong>Consistency matters.</strong> Having a reproducible command chain eliminates human error—no more “oops, I used the wrong flag or command”</li>
<li><strong>Convenience.</strong> With a single argument (<code>&lt;vm-name&gt;</code>) the script does all the heavy lifting, timestamps the output, ready for you to analyze the <code>.pcap</code>output.</li>
</ul>
<p> </p>
<h3>How to Use It</h3>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token">chmod</span> +x capture_vm_switchport_traffic.sh<br>./capture_vm_switchport_traffic.sh my-vm</code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>Replace <code>my-vm</code> with any unique substring of the VM’s name. The script will:</p>
<ol>
<li>Find the correct switch‑port.</li>
<li>Start capturing traffic.</li>
<li>Save a timestamped <code>.pcap</code> under <code>/tmp</code>.</li>
</ol>
<p>When you’re done, simply press <strong>Ctrl‑C</strong>. The capture ends, and you’ll see the location of the file printed to the console.</p>
<h3>The Script, Line by Line</h3>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<div><code>#!/bin/sh
# ---------------------------------------------------------------
# </code></div>
<div><code>capture_vm_switchport_traffic.sh
</code></div>
<div><code>#
# Usage:   ./capture_vm_switchport_traffic.sh &lt;vm-name&gt;
</code></div>
<div><code>#
# What it does:
</code></div>
<div><code>#   1️⃣  Finds the switch‑port ID (PortNum) for the given VM
</code></div>
<div><code>#       using `net‑stats -l`.  The VM name appears in column 6;
</code></div>
<div><code>#       we also capture the exact name reported by the system.
</code></div>
<div><code>#   2️⃣  Starts a packet capture on that switch‑port.
</code></div>
<div><code>#   3️⃣  Saves the capture file under /tmp with a timestamp.
</code></div>
<div><code># ---------------------------------------------------------------</code></div>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p><em>The header explains purpose, usage, and the three core steps.</em></p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<div><code>set -euo pipefail   # safer scripting</code></div>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p><em>Enables strict error handling: abort on any failure, treat unset variables as errors, and propagate failures through pipelines.</em></p>
<h4>1️⃣ Argument Validation</h4>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<div><code><code>if [ "$#" -ne 1 ]; then</code></code></div>
<div>  <code><code>echo "Error: Exactly one argument (the VM/client name) is required."</code></code></div>
<div>  <code><code>echo "Usage: $0 &lt;vm-name&gt;"
</code></code></div>
<div><code><code>exit 1
fi</code></code>INPUT_VM=$1 # what the user typed (may be partial)</div>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>We insist on a single argument – the (partial) VM name – and store it for later pattern matching.</p>
<h4>2️⃣ Locate the Switch‑Port</h4>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<div>
<p><code>NET_OUT=$(net-stats -l)</code></p>
<p>MATCH=$(printf "%s\n" "$NET_OUT" | awk -v pat="$INPUT_VM" 'tolower($6) ~ tolower(pat) {print $1, $6}' | head -n1)</p>
<p>PORT_NUM=$(printf "%s" "$MATCH" | awk '{print $1}') REAL_VM=$(printf "%s" "$MATCH" | awk '{print $2}' | cut -d '.' -f1)</p>
</div>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<ul>
<li><code>net‑stats -l</code> lists all live interfaces.</li>
<li>We pipe the output through <code>awk</code>, performing a case‑insensitive match against column 6 (the VM name).</li>
<li>The first match gives us two pieces of data: the <strong>PortNum</strong> (column 1) and the exact VM identifier.</li>
<li><code>cut -d '.' -f1</code> strips any domain suffix that might appear.</li>
</ul>
<p>If nothing matches, we bail out with a clear error:</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code>if [ -z "${PORT_NUM:-}" ] || [ -z "${REAL_VM:-}" ]; then<br>    echo "Error: No matching entry found for client \"$INPUT_VM\"."    exit 2fi</code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<h4>3️⃣ Timestamped Filename</h4>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code>TIMESTAMP=$(date +%Y%m%d_%H%M%S)PCAP_FILE="/tmp/${REAL_VM}_${TIMESTAMP}.pcap"</code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>A unique filename prevents accidental overwrites and makes it easy to locate captures later.</p>
<h4>4️⃣ Run the Capture</h4>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code>echo "Starting capture on switch‑port $PORT_NUM → $PCAP_FILE"<br>echo "Press Ctrl‑C to stop the capture."<br>( pktcap-uw --switchport "$PORT_NUM" --dir 2 -o - | tcpdump-uw -r - -w "$PCAP_FILE")</code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<ul>
<li><code>pktcap-uw</code> grabs raw packets from the specified switch‑port (<code>--dir 2</code> means both inbound and outbound).</li>
<li>The <code>-o -</code> flag streams the output to stdout, which we pipe directly into <code>tcpdump-uw</code>.</li>
<li><code>tcpdump-uw</code> reads from stdin (<code>-r -</code>) and writes a proper <code>.pcap</code> file (<code>-w</code>).</li>
</ul>
<p>Because the whole pipeline runs inside a subshell, pressing <strong>Ctrl‑C</strong> cleanly terminates both processes.</p>
<h4>5️⃣ Wrap‑Up Message</h4>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code>echo "Capture finished. PCAP saved to: $PCAP_FILE"<br>echo "You can inspect it with:"<br>echo "    tcpdump-uw -r $PCAP_FILE"<br>echo "    wireshark $PCAP_FILE   # after copying to a workstation with Wireshark"</code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>A friendly reminder of the next steps: analyze with <code>tcpdump</code> on the host or pull the file to a workstation and open it in Wireshark.</p>
<p> </p>
<h3>Takeaways</h3>
<ul>
<li><strong>One source of truth.</strong> By encapsulating the lookup and capture logic, you eliminate the mental overhead of remembering several unrelated commands.</li>
<li><strong>Safety first.</strong> <code>set -euo pipefail</code> ensures the script stops on any unexpected condition, protecting you from generating empty or misleading captures.</li>
<li><strong>Reusability.</strong> Ready to troubleshoot VMs instantly.</li>
</ul>
<p>Next time you’re staring at a VM and need to see what’s really happening on the wire, just run the script and let it do the heavy lifting. Happy sniffing! 🐶</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>Automating VMware/Broadcom URL Whitelisting Checks with a Simple Bash Script</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/automating-vmwarebroadcom-url-whitelisting-checks-with-a-simple-bash-script/"/>
        <id>https://MichaelRyom.dk/automating-vmwarebroadcom-url-whitelisting-checks-with-a-simple-bash-script/</id>

        <updated>2025-09-08T03:38:14+02:00</updated>
            <summary type="html">
                <![CDATA[
                    I wrote another piece of bash script that I think others might fine useful, hence the blog post :) It is some what inspired by&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p>I wrote another piece of bash script that I think others might fine useful, hence the blog post :) It is some what inspired by DSvClient which I also recently did a new release of check it out <a href="https://michaelryom.dk/dsvclient-06-broadcom-ready-and-bug-fixes/" target="_blank" rel="noopener">here on MichaelRyom.dk</a> or <a href="https://github.com/MichaelRyom/DSvClient" target="_blank" rel="noopener">here on GitHub</a>!</p>
<h4>Why I wrote this script</h4>
<p>I ran into issues working with vCenter and network security. It was a very practical problem: <strong>firewall and proxy rules were silently breaking essential VMware/Broadcom services</strong>.</p>
<p>Broadcom publishes a <em>public URL list</em> that must be whitelisted for things like vSphere Update Manager, vSAN Cloud Health, and the VVS API. The list lives here:</p>
<p>🔗 <a href="https://knowledge.broadcom.com/external/article/327186/public-url-list-for-sddc-manager.html">https://knowledge.broadcom.com/external/article/327186/public-url-list-for-sddc-manager.html</a></p>
<p>Back in the day my go‑to method was a blunt‑force <code>curl -k &lt;url&gt; -vvv</code> and then manually scanning the verbose output for errors or a successful <code>200 OK</code> or finding proxy denies. That works, but it’s noisy, time‑consuming, and impossible to run reliably across dozens of vCenters.</p>
<p>I wanted a <strong>single, repeatable command</strong> that would:</p>
<ul>
<li>Hit every URL on the official whitelist (that I and most need).</li>
<li>Show a concise status (HTTP code + short note - I broke the note part a some point, so mainly HTTP code).</li>
<li>Handle the one oddball Broadcom VVS endpoint that requires an OAuth bearer token.</li>
</ul>
<p>That’s exactly what the script does. It condenses the whole checklist into a tidy table that anyone can read at a glance.</p>
<p><strong>By the way if you wanna jump straight to the script it is available <a href="https://codeberg.org/MichaelRyom/VMware_Bash_Scripts/src/branch/main/VMware_Broadcom_URL_Check.sh" target="_blank" rel="noopener">here on codeberg.org</a></strong></p>
<p> </p>
<h4>What the script actually does</h4>
<p>Below is a high‑level walk‑through of the script’s flow. I’ve kept the code deliberately short and commented, so you can see the logic without getting lost in Bash minutiae.</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token shebang">#!/usr/bin/env bash</span><span class="token">set</span> -euo pipefail          <br><span class="token"># Fail fast on errors<br></span><span class="token"># -------------------------------------------------------------------------<br></span><span class="token"># 1️⃣  Arguments – you must supply the &lt;download-token&gt; that replaces the</span><span class="token">#     placeholder in one of the URLs.<br></span><span class="token"># -------------------------------------------------------------------------<br></span><span class="token">if</span> <span class="token">[</span><span class="token">[</span> <span class="token">$#</span> -ne <span class="token">1</span> <span class="token">]</span><span class="token">]</span><span class="token">;</span> <span class="token">then<br></span>    <span class="token">echo</span> <span class="token">"Usage: </span><span class="token">$0</span><span class="token"> &lt;download-token&gt;"<br></span>    <span class="token">exit</span> <span class="token">1<br></span><span class="token">fi<br><br></span><span class="token assign-left">DOWNLOAD_TOKEN</span><span class="token">=</span><span class="token">"</span><span class="token">$1</span><span class="token">"</span></code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<h5>1. Embedded URL list (TOML)</h5>
<p>The URLs are stored in a small TOML block right inside the script. Each line follows the same <code>url = "…" </code> syntax, which makes it easy to add or remove entries later.</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token assign-left">TOML</span><span class="token">=</span><span class="token">$(</span><span class="token">cat</span> <span class="token">&lt;&lt;</span><span class="token">'EOS'</span><span class="token">url = "https://dl.broadcom.com/&lt;downloadToken&gt;/PROD/COMP/ESX_HOST/addon-main/vmw-depot-index.xml"</span><span class="token">url = "https://partnerweb.vmware.com/service/vsan/all.json"</span><span class="token">url = "https://vcsa.vmware.com/ph/api/v1/results?deploymentId=2d02e861-7e93-4954-9a73-b08692a330d1&amp;collectorId=VsanCloudHealth.6_5&amp;objectId=0c3e9009-ba5d-4e5f6-bae8-f25ec506d219&amp;type=vsan-updates-json"</span><span class="token">url = "https://vvs.broadcom.com/v1/compatible/vcg/bundles/all?format=gz"</span><span class="token">EOS</span><span class="token">)</span></code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<h5>2. Pull out just the URL lines</h5>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token">mapfile</span> -t URL_LINES <span class="token">&lt;</span> <span class="token">&lt;</span><span class="token">(</span><span class="token">grep</span> -E <span class="token">'^url[[:space:]]*='</span> <span class="token">&lt;&lt;&lt;</span><span class="token">"</span><span class="token">$TOML</span><span class="token">"</span><span class="token">)</span></code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>Now <code>URL_LINES</code> holds each definition, ready for processing.</p>
<h5>3. Detect whether we need an OAuth token</h5>
<p>Only the <code>vvs.broadcom.com</code> endpoint requires a bearer token. The script scans the list; if it finds such a URL it performs a <strong>client‑credentials grant</strong> against Broadcom’s auth service.</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token">if</span> <span class="token">$needs_oauth</span><span class="token">;</span> <span class="token">then</span>    <span class="token assign-left">token_resp</span><span class="token">=</span><span class="token">$(</span><span class="token">curl</span><span class="token"> -s -X POST </span><span class="token">"</span><span class="token">$AUTH_URL</span><span class="token">"</span> <span class="token">\</span><span class="token">        -H </span><span class="token">'Content-Type: application/json'</span> <span class="token">\</span><span class="token">        -d </span><span class="token">"</span><span class="token">$(</span><span class="token">jq -nc --arg cid </span><span class="token">"</span><span class="token">$CLIENT_ID</span><span class="token">"</span><span class="token"> --arg cs </span><span class="token">"</span><span class="token">$CLIENT_SECRET</span><span class="token">"</span> <span class="token">\</span>                <span class="token">'{grant_type:"client_credentials",client_id:$cid,client_secret:$cs}'</span><span class="token">)</span><span class="token">"</span><span class="token">)</span>    <span class="token assign-left">ACCESS_TOKEN</span><span class="token">=</span><span class="token">$(</span><span class="token">jq -r </span><span class="token">'.access_token // empty'</span> <span class="token">&lt;&lt;&lt;</span><span class="token">"</span><span class="token">$token_resp</span><span class="token">"</span><span class="token">)</span><span class="token">fi</span></code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<h5>4. Probe each URL</h5>
<p>The heart of the script is the <code>probe()</code> helper. It tries a lightweight <code>HEAD</code> request first (fast, no body). If the response isn’t a 2xx, it falls back to a silent <code>GET</code>. The function returns a string like <code>200|OK</code>.</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token function-name">probe</span><span class="token">(</span><span class="token">)</span> <span class="token">{</span>    <span class="token">local</span> <span class="token assign-left">url</span><span class="token">=</span><span class="token">$1</span> <span class="token assign-left">extra</span><span class="token">=</span><span class="token">${2</span><span class="token">:-</span><span class="token">}</span>    <span class="token">local</span> http_code note    <span class="token assign-left">http_code</span><span class="token">=</span><span class="token">$(</span><span class="token">curl</span><span class="token"> --silent --show-error --head --max-time $MAX_TIME </span><span class="token">\</span><span class="token">                     $extra </span><span class="token">"</span><span class="token">$url</span><span class="token">"</span><span class="token"> -w </span><span class="token">"%{http_code}"</span><span class="token"> -o /dev/null</span><span class="token">)</span> <span class="token">||</span> <span class="token">true</span>    …<span class="token">}</span></code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>For the OAuth‑protected VVS endpoint we send the token in a custom header (<code>X-Vmw-Esp-Client</code>) and parse the first response line.</p>
<h5>5. Print a compact table</h5>
<p>Finally the script prints a nicely aligned table:</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code><span class="token">printf</span> <span class="token">"</span><span class="token">\n</span><span class="token">%-95s %-6s %s</span><span class="token">\n</span><span class="token">"</span> <span class="token">"Result"</span> <span class="token">"Code"</span> <span class="token">"Note"</span><span class="token">printf</span> <span class="token">"%-95s %-6s %s</span><span class="token">\n</span><span class="token">"</span> <span class="token">"</span><span class="token">$(</span><span class="token">printf</span> <span class="token">'=%.0s'</span> <span class="token">{</span><span class="token">1</span><span class="token">..</span><span class="token">95</span><span class="token">}</span><span class="token">)</span><span class="token">"</span> <span class="token">"------"</span> <span class="token">"----"</span>…<span class="token">printf</span> <span class="token">"%-95s %-6s %s</span><span class="token">\n</span><span class="token">"</span> <span class="token">"</span><span class="token">$src_url</span><span class="token">"</span> <span class="token">"</span><span class="token">$code</span><span class="token">"</span> <span class="token">"</span><span class="token">$note</span><span class="token">"</span></code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<p>The output looks like this (example run with a dummy token):</p>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<blockquote>
<pre><code>Result Code Note=============================================================================================== ------ ----https://dl.broadcom.com/&lt;downloadToken&gt;/PROD/COMP/ESX_HOST/addon-main/vmw-depot-index.xml 403 403 https://partnerweb.vmware.com/service/vsan/all.json 200 200 https://vcsa.vmware.com/ph/api/v1/results?deploymentId=2d02e861-7e93-4954-9a73-b08692a330d1&amp;collectorId=VsanCloudHealth.6_5&amp;objectId=0c3e9009-ba5d-4e5f6-bae8-f25ec506d219&amp;type=vsan-updates-json 200 405 https://vvs.broadcom.com/v1/compatible/vcg/bundles/all?format=gz 200 200</code></pre>
</blockquote>
</div>
<div class="lumo-no-copy flex-none z-10"> </div>
</div>
<ul>
<li><strong>Result</strong> – The full URL that was tested (the <code>&lt;downloadToken&gt;</code> placeholder is replaced by the token you passed, but not viable in output).</li>
<li><strong>Code</strong> – The HTTP status code returned by the server. Anything in the 200‑range means the endpoint is reachable; 3xx indicates a redirect, 4xx/5xx signals a problem.</li>
<li><strong>Note</strong> – SHOULD HAVE BEEN: The short description taken from the first line of the HTTP response (e.g., “OK”, “Moved Permanently”, “Unauthorized”). It gives a quick hint about why a non‑200 code appeared.</li>
</ul>
<h2> </h2>
<h4>How to use the script</h4>
<p><strong>Make it executable</strong></p>
<blockquote>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="flex-auto pr-4">
<div><code><span class="token">chmod</span> +x vmware_broadcom_url_check.sh</code></div>
</div>
<div class="lumo-no-copy flex-none z-10">
<p> </p>
</div>
</div>
</blockquote>
<div class="message-container code-container flex flex-row flex-nowrap relative">
<div class="lumo-no-copy flex-none z-10">
<p><strong>Run it, passing the download token you got from Broadcom</strong></p>
</div>
</div>
<blockquote>
<div><code>./vmware_broadcom_url_check.sh <span class="token">&lt;</span>your-download-token<span class="token">&gt;</span></code></div>
</blockquote>
<p style="text-align: left;">Replace <code>&lt;your-download-token&gt;</code> with the real token (or a test value if you just want to see the table format).</p>
<p> </p>
<p style="text-align: left;"><strong>Read the table</strong> – any row that isn’t <code>200 OK</code> is a candidate for firewall or proxy adjustment.</p>
<h4>Keeping the URL list up‑to‑date</h4>
<p>The official list lives at:</p>
<p>🔗 <a href="https://knowledge.broadcom.com/external/article/327186/public-url-list-for-sddc-manager.html" target="_blank" rel="noopener">https://knowledge.broadcom.com/external/article/327186/public-url-list-for-sddc-manager.html</a></p>
<p>Broadcom occasionally adds or deprecates entries. Whenever you spot a change, simply update the TOML block in the script and re‑run. Because the script is self‑contained, there’s no external configuration file to manage.</p>
<h4>Final thoughts</h4>
<p>What started as a handful of manual <code>curl</code> commands turned into a <strong>repeatable, auditable health‑check</strong> that I now run whenever I touch firewall rules, spin up a new vCenter instance.</p>
<p>If you’re managing vSphere, vSAN, or any Broadcom‑backed SDDC component, I hope you find this script as useful as I do. Feel free to fork it, tweak the output format, or integrate it into your own monitoring stack. A big shout‑out to my other project DSvClient which started this for me.</p>
<p>Happy testing! 🚀</p>
            ]]>
        </content>
    </entry>
    <entry>
        <title>DSvClient 0.6 - Broadcom ready and bug fixes</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/dsvclient-06-broadcom-ready-and-bug-fixes/"/>
        <id>https://MichaelRyom.dk/dsvclient-06-broadcom-ready-and-bug-fixes/</id>

        <updated>2025-09-03T22:13:14+02:00</updated>
            <summary type="html">
                <![CDATA[
                    Hi, Since the initial release, I've been addressing quite a few bugs that came with the new Broadcom token, as well as other minor issues&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p>Hi,</p>
<p>Since the initial release, I've been addressing quite a few bugs that came with the new Broadcom token, as well as other minor issues and slowdowns.</p>
<p>I've also open-sourced the software. It's still not perfect, but hey, whose code that, right? 😉</p>
<p>Going forward, I'll be supporting Windows and Linux only. If you need it for other operating systems, you'll have to build it yourself.</p>
<p>The latest version, 0.6, can be found here: <a href="https://github.com/MichaelRyom/DSvClient/tree/Main/releases/latest" target="_blank" rel="noopener">https://github.com/MichaelRyom/DSvClient/tree/Main/releases/latest</a></p>
<p>I've implemented a new system for GitHub automation to help with releases when needed.</p>
<p>Here are some of the key improvements and fixes:</p>
<ul>
 	
<li>Instead of maintaining a list of files that were not available, this release creates a file with possible exclusions if a file is not present. You can enable this in the config.toml file. This way, you won't see as many 403/Access denied errors, and these files won't be reported on.</li>
<li>I've made output and exit codes more useful and require less manual monitoring.</li>
<li>I've added a new URL for the VVS compatibility data API, which is a redirect to where Broadcom has saved the latest VCG Compatibility bundle. It used to be on AWS S3, but has recently been moved to Google Storage.</li>
<li>The command-line interface now shows both version and help information, which is very nice and much-needed.</li>
</ul>
<p>
For more details, check out the GitHub repository: <a href="https://github.com/MichaelRyom/DSvClient" target="_blank" rel="noopener">https://github.com/MichaelRyom/DSvClient</a></p>

            ]]>
        </content>
    </entry>
    <entry>
        <title>DSvClient - New patch downloading tool for vCenter</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/dsvclient-new-patch-downloading-tool-for-vcenter/"/>
        <id>https://MichaelRyom.dk/dsvclient-new-patch-downloading-tool-for-vcenter/</id>

        <updated>2025-04-18T08:07:32+02:00</updated>
            <summary type="html">
                <![CDATA[
                    Streamlining VMware Update Management with DSvClient: A vExpert's tool In the ever-evolving landscape of VMware infrastructure management, keeping components updated while ensuring consistency across environments&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <h2 style="padding-left: 40px;"> </h2>
<h2>Streamlining VMware Update Management with DSvClient: A vExpert's tool</h2>
<p>In the ever-evolving landscape of VMware infrastructure management, keeping components updated while ensuring consistency across environments has become increasingly complex. As a VMware administrator, you're likely familiar with the challenges of managing patches, updates, and ensuring file integrity across multiple environments. Today, I'd like to introduce you to a powerful utility that I have created.</p>
<div>I started this project for fun while experimenting with Rust during last year's Christmas holiday. It has since evolved into an application that I believe has multiple applications in enterprise environments.</div>
<div>
<h3 style="text-align: left;">What Is DSvClient?</h3>
<div>DSvClient is a lightweight, cross-platform command-line utility designed specifically for VMware administrators who need to efficiently manage update repositories, verify file integrity, and ensure consistent deployment of VMware components. Built with performance in mind using Rust, DSvClient offers remarkable speed even when processing large repositories or verifying extensive file content.</div>
<div>Available for Windows, Linux, macOS, and even FreeBSD, DSvClient can be downloaded from <a href="https://codeberg.org/MichaelRyom/DSvClient">Codeberg</a>, making it accessible regardless of your management platform.</div>
</div>
<div> </div>
<div>Click OS icon to download DSvClient for your OS</div>
<div> </div>
<div><a href="https://codeberg.org/MichaelRyom/DSvClient/raw/branch/main/Release/0.3.0/DSvClient-freebsd.zip" target="_blank" rel="noopener"><figure class="alignnone wp-image-2131 size-thumbnail"><img  src="https://MichaelRyom.dk/media/posts/129/free-bsd-150x150.png" alt="" width="150" height="150"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/129/responsive/free-bsd-150x150-xs.png 300w ,https://MichaelRyom.dk/media/posts/129/responsive/free-bsd-150x150-sm.png 480w ,https://MichaelRyom.dk/media/posts/129/responsive/free-bsd-150x150-md.png 768w ,https://MichaelRyom.dk/media/posts/129/responsive/free-bsd-150x150-lg.png 1024w"></figure></a> <a href="https://codeberg.org/MichaelRyom/DSvClient/raw/branch/main/Release/0.3.0/DSvClient-linux.zip" target="_blank" rel="noopener"><figure class="alignnone wp-image-2132 size-thumbnail"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/129/linux-150x150.png" alt="" width="150" height="150"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/129/responsive/linux-150x150-xs.png 300w ,https://MichaelRyom.dk/media/posts/129/responsive/linux-150x150-sm.png 480w ,https://MichaelRyom.dk/media/posts/129/responsive/linux-150x150-md.png 768w ,https://MichaelRyom.dk/media/posts/129/responsive/linux-150x150-lg.png 1024w"></figure></a> <a href="https://codeberg.org/MichaelRyom/DSvClient/raw/branch/main/Release/0.3.0/DSvClient-windows.zip" target="_blank" rel="noopener"><figure class="alignnone wp-image-2133 size-thumbnail"><img  src="https://MichaelRyom.dk/media/posts/129/windows1-150x150.png" alt="" width="150" height="150"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/129/responsive/windows1-150x150-xs.png 300w ,https://MichaelRyom.dk/media/posts/129/responsive/windows1-150x150-sm.png 480w ,https://MichaelRyom.dk/media/posts/129/responsive/windows1-150x150-md.png 768w ,https://MichaelRyom.dk/media/posts/129/responsive/windows1-150x150-lg.png 1024w"></figure></a> <a href="https://codeberg.org/MichaelRyom/DSvClient/raw/branch/main/Release/0.3.0/DSvClient-macos.zip" target="_blank" rel="noopener"><figure class="alignnone wp-image-2134 size-thumbnail"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/129/t_macos6983-150x150.jpg" alt="" width="150" height="150"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/129/responsive/t_macos6983-150x150-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/129/responsive/t_macos6983-150x150-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/129/responsive/t_macos6983-150x150-md.jpg 768w ,https://MichaelRyom.dk/media/posts/129/responsive/t_macos6983-150x150-lg.jpg 1024w"></figure></a></div>
<div> </div>
<div>
<p><a href="https://codeberg.org/MichaelRyom/DSvClient/raw/branch/main/config.toml" target="_blank" rel="noopener">Download config.toml</a></p>
<p><a href="https://codeberg.org/MichaelRyom/DSvClient/raw/branch/main/sources.toml" target="_blank" rel="noopener">Download sources.toml</a></p>
<h3>Why DSvClient Matters</h3>
<div>If you've ever managed a disconnected vSphere environment or needed to maintain consistent patch levels across multiple sites, you'll immediately understand DSvClient's value. Here are the key reasons why this tool deserves a place in your VMware toolkit:</div>
<ol>
<li><strong>Offline Repository Management</strong> Perfect for air-gapped environments where direct internet access is prohibited.</li>
<li><strong>File Integrity Verification</strong> Automatically verifies checksums of downloaded components, ensuring you're deploying exactly what VMware released.</li>
<li><strong>Consistent Update Strategy</strong> Simplifies maintaining identical patch levels across development, testing, and production environments.</li>
<li><strong>Bandwidth Optimization</strong> Download once, deploy many times, dramatically reducing bandwidth usage in multi-site deployments.</li>
<li><strong>Automation-Friendly</strong> Easily integrated into CI/CD pipelines and automation workflows.</li>
</ol>
<h3>Key Use Cases for DSvClient</h3>
<h4>    1. Creating and Maintaining Offline Update Repositories</h4>
<div>Many enterprise environments, particularly in financial, healthcare, and government sectors, operate in restricted network environments. DSvClient excels in these scenarios:</div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>DSvClient C:\VMware\UpdateRepo<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div>With this simple command, DSvClient will download the latest patches and updates from the configured sources, creating a perfectly structured, local repository that can be used by Update Manager or other deployment tools.</div>
<div> </div>
<div> </div>
<h4>     2. Validating Update Repository Integrity</h4>
<div>Have you ever deployed an update only to discover file corruption midway through? DSvClient's verification capabilities prevent this nightmare scenario:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>DSvClient C:\VMware\UpdateRepo --verify<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div>This command meticulously checks each file against its expected checksum, producing a detailed report of any inconsistencies. It has saved me countless hours of troubleshooting corrupted installations.</div>
<div> </div>
<div> </div>
<h4>      3. Maintaining Consistent vSphere Environments</h4>
<div>For organizations with development, testing, and production environments, consistency is critical. I use DSvClient to ensure identical patch baselines across all environments:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code># Download once <restore-line-break>DSvClient /mnt/shared/VMware/Updates<restore-line-break># Verify before deployment to each environment<restore-line-break>DSvClient /mnt/shared/VMware/Updates --verify<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<h4>      4. Complete vCenter Upgrade Management</h4>
<div>One of my favorite features is DSvClient's ability to handle VCSA upgrade paths:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code># Configure DSvClient to download specific VCSA versions<restore-line-break>vim sources.toml<restore-line-break># Download all necessary VCSA components<restore-line-break>DSvClient /storage/vcsa-updates<restore-line-break></code></pre>
<p>
<!-- /wp:code --></div>
<div> </div>
<div>
<div>This allows you to maintain a complete library of VCSA upgrade artifacts, ready for deployment when needed.</div>
<div> </div>
<h4>      5. Centralized vSAN HCL Management</h4>
<div>Managing the vSAN Hardware Compatibility List offline has traditionally been challenging. DSvClient simplifies this:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code># The sources.toml already includes vSAN HCL sources<restore-line-break>DSvClient /path/to/vsan-repository<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div>Now your vSAN health checks can reference a local, always-available HCL list and catalog.</div>
<h3>Getting Started with DSvClient</h3>
<div>Starting with DSvClient is straightforward, making it accessible even for administrators new to command-line tools:</div>
<div> </div>
<div>      1. <strong>Download the appropriate binary</strong> for your platform from <a href="https://codeberg.org/MichaelRyom/DSvClient">Codeberg</a>.</div>
<div>      2. <strong>Configure your sources</strong> by customizing the sources.toml file. The default configuration includes:</div>
<ul>
<li>ESXi base updates and patches</li>
<li>Vendor and partner provided add-ons</li>
<li>VMware certified async drivers</li>
<li>vSAN HCL list and catalog</li>
<li>VCSA upgrade paths</li>
</ul>
<div>      3. <strong>Run your first repository synchronization</strong>:</div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>DSvClient /path/to/repository<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div>      4. <strong>Verify your repository</strong>:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>DSvClient /path/to/repository --verify<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div>      5. <strong>Customize behavior</strong> through the config.toml file to adjust performance parameters based on your environment:</div>
<ul>
<li>Concurrent download limits</li>
<li>Verification thread count</li>
<li>Logging verbosity</li>
<li>Exclude patterns for unwanted components</li>
</ul>
<h3>Advanced Configuration Tips</h3>
<div>After using DSvClient extensively in various environments, I've found these configuration adjustments particularly helpful:</div>
<div>      1. <strong>Tune concurrent downloads</strong> based on your internet connection:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>[download]<restore-line-break>max_concurrent_downloads = 10<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div> </div>
<div>      2. <strong>Optimize verification on powerful systems</strong>:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>[verification]<restore-line-break>max_concurrent_verifications = 1000<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<div> </div>
<div> </div>
<div>      3. <strong>Exclude unnecessary components</strong> to save space and time:</div>
<p>
These are default, as they are not available on the VMware download site. <!-- wp:code --></p>
<pre class="wp-block-code"><code>[exclude]<restore-line-break>patterns = [<restore-line-break>      "addon-main/addon/NEC/vib20/qedentv/QLC_bootbank_qedentv_3.40.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qedf/QLC_bootbank_qedf_2.2.4.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qedi/QLC_bootbank_qedi_2.19.5.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qedrntv/QLC_bootbank_qedrntv_3.40.9.1-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/smx-provider/HPE_bootbank_smx-provider_700.03.16.00.12-14828939.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/i40en/INT_bootbank_i40en_1.10.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/lpfc/EMU_bootbank_lpfc_12.6.228.4-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qfle3f/QLC_bootbank_qfle3f_2.1.6.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qcnic/QLC_bootbank_qcnic_2.0.0.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qfle3/QLC_bootbank_qfle3_1.4.6.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qfle3i/QLC_bootbank_qfle3i_2.1.2.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/amscli/HPE_bootbank_amscli_11.5.0.22-1OEM.700.0.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/amsd/HPE_bootbank_amsd_700.11.5.0.28-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/ilo/HPE_bootbank_ilo_700.10.1.0.16-1OEM.700.0.0.14828939.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/ixgben/INT_bootbank_ixgben_1.8.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/igbn/INT_bootbank_igbn_1.4.11.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/NEC/vib20/qlnativefc/Marvell_bootbank_qlnativefc_4.1.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qedentv/QLC_bootbank_qedentv_3.40.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qedi/QLC_bootbank_qedi_2.19.5.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qedrntv/QLC_bootbank_qedrntv_3.40.9.1-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qedf/QLC_bootbank_qedf_2.2.4.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/sut/HPE_bootbank_sut_700.2.5.5-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/smx-provider/HPE_bootbank_smx-provider_700.03.16.00.12-14828939.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/hpessacli/HPE_bootbank_hpessacli_4.18.1.0-7.0.0.15525992.hpe.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/lpfc/EMU_bootbank_lpfc_12.6.228.4-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/ilo/HPE_bootbank_ilo_700.10.1.0.16-1OEM.700.0.0.14828939.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/amsd/HPE_bootbank_amsd_700.11.5.0.28-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/amscli/HPE_bootbank_amscli_11.5.0.22-1OEM.700.0.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qfle3i/QLC_bootbank_qfle3i_2.1.2.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qfle3f/QLC_bootbank_qfle3f_2.1.6.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qcnic/QLC_bootbank_qcnic_2.0.0.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qfle3/QLC_bootbank_qfle3_1.4.6.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/qlnativefc/Marvell_bootbank_qlnativefc_4.1.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/ixgben/INT_bootbank_ixgben_1.8.9.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/bootcfg/HPE_bootbank_bootcfg_700.10.5.0.23-7.0.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/hponcfg/HPE_bootbank_hponcfg_700.10.5.0.25-7.0.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/testevent/HPE_bootbank_testevent_700.10.5.0.24-7.0.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/conrep/HPE_bootbank_conrep_700.10.5.0.34-7.0.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/cru/HPE_bootbank_cru_700.10.16-1OEM.700.0.0.14828939.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/igbn/INT_bootbank_igbn_1.4.11.0-1OEM.700.1.0.15525992.vib",<restore-line-break>      "addon-main/addon/HTI/vib20/fc-enablement/HPE_bootbank_fc-enablement_700.3.5.0.40-1OEM.700.0.0.15525992.vib",<restore-line-break>]<restore-line-break></code></pre>
<p>
<!-- /wp:code --></div>
<div> </div>
<div> </div>
<div>
<div>      4. <strong>Enhance logging for troubleshooting</strong>:</div>
<div> </div>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>[logging]<restore-line-break>term_level = "Debug"<restore-line-break></code></pre>
<p>
<!-- /wp:code --></p>
<h3>Integrating with Automation</h3>
<div>DSvClient truly shines when integrated into automation workflows. You can incorporated it into your monthly patch management process:</div>
<ol>
<li><strong>Scheduled download</strong> of updates using a simple cron job or Task Scheduler task</li>
<li><strong>Automatic verification</strong> after download completion</li>
<li><strong>Integration with Update Manager</strong> through internal webserver</li>
<li><strong>Notifications</strong> of download completion and verification status</li>
</ol>
<div>The consistent exit codes and structured output make DSvClient particularly suitable for automation scenarios, enabling fully unattended update management.</div>
</div>
<div> </div>
<p>
<!-- wp:heading --></p>
<h2>DSvClient Application Flow Diagram</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>To understand how DSvClient works, here's a visual representation of the application's flow from configuration to repository creation:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code>┌────────────────────────────────────┐<restore-line-break>│ Configuration Files                │<restore-line-break>│ ┌──────────────┐  ┌──────────────┐ │<restore-line-break>│ │ config.toml  │  │ sources.toml │ │<restore-line-break>│ │  - Download  │  │  - VMware    │ │<restore-line-break>│ │    settings  │  │    updates   │ │<restore-line-break>│ │  - Verify    │  │  - VCSA      │ │<restore-line-break>│ │    settings  │  │    versions  │ │<restore-line-break>│ │  - Logging   │  │  - vSAN HCL  │ │<restore-line-break>│ │  - Exclusions│  │  - Vendor    │ │<restore-line-break>│ └──────────────┘  │    add-ons   │ │<restore-line-break>│                   └──────────────┘ │<restore-line-break>└───────────────────┬────────────────┘<restore-line-break>                    │<restore-line-break>                    ▼<restore-line-break>┌────────────────────────────────────┐<restore-line-break>│ DSvClient Core Process             │<restore-line-break>│ ┌──────────────────────────────┐   │<restore-line-break>│ │ 1. Parse configuration       │   │<restore-line-break>│ │ 2. Initialize components     │   │<restore-line-break>│ │ 3. Set up logging            │   │<restore-line-break>│ │ 4. Process command arguments │   │<restore-line-break>│ └──────────────┬───────────────┘   │<restore-line-break>│                │                   │<restore-line-break>│                ▼                   │<restore-line-break>│ ┌──────────────────────────────┐   │<restore-line-break>│ │         Downloader           │   │<restore-line-break>│ │ ┌────────────────────────┐   │   │<restore-line-break>│ │ │ ESXi Updates Pipeline  │   │   │<restore-line-break>│ │ └────────────────────────┘   │   │<restore-line-break>│ │ ┌────────────────────────┐   │   │<restore-line-break>│ │ │ VCSA Updates Pipeline  │   │   │<restore-line-break>│ │ └────────────────────────┘   │   │<restore-line-break>│ │ ┌────────────────────────┐   │   │<restore-line-break>│ │ │ vSAN HCL Pipeline      │   │   │<restore-line-break>│ │ └────────────────────────┘   │   │<restore-line-break>│ │ ┌────────────────────────┐   │   │<restore-line-break>│ │ │ Vendor Add-on Pipeline │   │   │<restore-line-break>│ │ └────────────────────────┘   │   │<restore-line-break>│ └──────────────┬───────────────┘   │<restore-line-break>│                │                   │<restore-line-break>│                ▼                   │<restore-line-break>│ ┌──────────────────────────────┐   │<restore-line-break>│ │          Verifier            │   │<restore-line-break>│ │ - SHA-1/SHA-256 verification │   │<restore-line-break>│ │ - Parallel processing        │   │<restore-line-break>│ │ - Error tracking             │   │<restore-line-break>│ └──────────────────────────────┘   │<restore-line-break>└────────────────┬───────────────────┘<restore-line-break>                 │<restore-line-break>                 ▼<restore-line-break>┌────────────────────────────────────┐<restore-line-break>│ Local Repository Structure          │<restore-line-break>│ /download_path/                    │<restore-line-break>│ ├── depot/                         │<restore-line-break>│ │   ├── index.xml                  │<restore-line-break>│ │   ├── vmw-depot-index-*.xml      │<restore-line-break>│ │   └── com.vmware.vsan.*          │<restore-line-break>│ │                                  │<restore-line-break>│ ├── addon-main/                    │<restore-line-break>│ │   ├── index.xml                  │<restore-line-break>│ │   └── addon/                     │<restore-line-break>│ │       ├── HTI/ (HPE)             │<restore-line-break>│ │       ├── DELL/                  │<restore-line-break>│ │       └── Other vendors...       │<restore-line-break>│ │                                  │<restore-line-break>│ ├── hcl.json                       │<restore-line-break>│ │                                  │<restore-line-break>│ └── vcsa/                          │<restore-line-break>│     └── version folders (e.g. 8.0.2)│<restore-line-break>│        ├── manifest-latest.xml     │<restore-line-break>│        └── vmware-vcsa-* files     │<restore-line-break>└──────────────────┬─────────────────┘<restore-line-break>                   │<restore-line-break>                   ▼<restore-line-break>┌────────────────────────────────────┐<restore-line-break>│ Optional Web Server Deployment     │<restore-line-break>│ ┌──────────────┐  ┌──────────────┐ │<restore-line-break>│ │ Apache/NGINX │  │  Docker      │ │<restore-line-break>│ └──────┬───────┘  └────┬─────────┘ │<restore-line-break>│        │               │           │<restore-line-break>│        └───────┬───────┘           │<restore-line-break>│                │                   │<restore-line-break>│                ▼                   │<restore-line-break>│ ┌──────────────────────────────┐   │<restore-line-break>│ │ Internal Web Server Endpoint │   │<restore-line-break>│ │ http://updates.internal.com/ │   │<restore-line-break>│ └──────────────────────────────┘   │<restore-line-break>└────────────────┬───────────────────┘<restore-line-break>                 │<restore-line-break>                 ▼<restore-line-break>┌────────────────────────────────────┐<restore-line-break>│ Consumption Methods                │<restore-line-break>│ ┌──────────────┐  ┌──────────────┐ │<restore-line-break>│ │ vCenter      │  │ Hierarchical │ │<restore-line-break>│ │ Update       │  │ Multi-Site   │ │<restore-line-break>│ │ Manager      │  │ Distribution │ │<restore-line-break>│ └──────────────┘  └──────────────┘ │<restore-line-break>└────────────────────────────────────┘</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:paragraph --></p>
<p>This diagram illustrates the complete flow of DSvClient from configuration to consumption:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list {"ordered":true} --></p>
<ol>
<li><strong>Configuration Files</strong>: DSvClient begins by reading two key configuration files:
<ul>
<li><code>config.toml</code> - Controls performance parameters, logging, and exclusions</li>
<li><code>sources.toml</code> - Defines what content to download and from where</li>
</ul>
</li>
<li><strong>Core Processing</strong>: The application initializes components, parses arguments, and prepares the environment</li>
<li><strong>Download Pipelines</strong>: Multiple specialized pipelines handle different content types:</p>
<ul>
<li>ESXi updates (patches, base images)</li>
<li>VCSA updates (appliance upgrades)</li>
<li>vSAN HCL (hardware compatibility lists)</li>
<li>Vendor-specific add-ons (drivers, utilities)</li>
</ul>
</li>
<li><strong>Verification</strong>: All downloaded content undergoes cryptographic verification:</p>
<ul>
<li>SHA-1 and SHA-256 checksum validation</li>
<li>Parallel processing for performance</li>
<li>Error tracking for failed verifications</li>
</ul>
</li>
<li><strong>Repository Structure</strong>: Content is organized into a VMware-compatible folder hierarchy</li>
<li><strong>Optional Web Deployment</strong>: The repository can be exposed via web servers for network distribution</li>
<li><strong>Consumption</strong>: The repository serves as a source for Update Manager or multi-site distribution</li>
</ol>
<p>
<!-- /wp:list --></p>
<p><!-- wp:paragraph --></p>
<p>The separation of concerns in DSvClient's architecture makes it efficient and maintainable. Each pipeline handles specific content types while sharing common infrastructure for downloading, verification, and error handling. This modular approach also allows for easy extension to support new content types or sources as VMware's ecosystem evolves.</p>
<p>
<!-- /wp:paragraph --></p>

<p>
<!-- /wp:code --></p>
<p><!-- wp:heading --></p>
<h2>DSvClient Repository Structure: A Visual Breakdown</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>When you run DSvClient with a download path, it creates an organized repository structure that mirrors VMware's official update repositories. Here's a visual breakdown of the typical folder structure you'll see based on the default sources.toml configuration:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code>/download_path<restore-line-break>│<restore-line-break>│<restore-line-break>├── addon-main                     # Hardware vendor add-ons<restore-line-break>│   ├── index.xml                  # Add-on index file<restore-line-break>│   └── addon<restore-line-break>│       ├── HTI                    # HP components<restore-line-break>│       │   └── vib20<restore-line-break>│       │       ├── ilo<restore-line-break>│       │       ├── amsd<restore-line-break>│       │       └── ... (other HP VIBs)<restore-line-break>│       │<restore-line-break>│       ├── DELL                   # Dell components<restore-line-break>│       │   └── vib20<restore-line-break>│       │       ├── iDRAC<restore-line-break>│       │       ├── BIOS<restore-line-break>│       │       └── ... (other Dell VIBs)<restore-line-break>│       │<restore-line-break>│       └── NEC                    # NEC components<restore-line-break>│           └── vib20<restore-line-break>│               └── ... (NEC VIBs)<restore-line-break>│<restore-line-break>└── ...<restore-line-break>│<restore-line-break>└── ...<restore-line-break>│<restore-line-break>└── ...<restore-line-break>│<restore-line-break>└── vcsa                           # vCenter Server Appliance updates<restore-line-break>    └── 8.0.2                      # Version-specific folders<restore-line-break>        ├── manifest-latest.xml    # Update manifest<restore-line-break>        ├── vmware-vcsa-8.0.2.0*   # VCSA ISO components<restore-line-break>        └── ... (other VCSA files)</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:paragraph --></p>
<p>This hierarchy is automatically created by DSvClient based on the sources defined in your sources.toml file. The default sources.toml includes entries for:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list --></p>
<ul>
<li><strong>ESXi Base Repository</strong> - Contains core ESXi updates and patches</li>
<li><strong>Hardware Vendor Add-ons</strong> - Drivers and utilities from partners like HPE, Dell and Cisco</li>
<li><strong>vSAN HCL Download</strong> - The vSAN Hardware Compatibility List</li>
<li><strong>VCSA Update Repositories</strong> - vCenter Server Appliance updates for various versions</li>
<li><strong>Async Driver Updates</strong> - Out-of-band driver releases from VMware</li>
</ul>
<p>
<!-- /wp:list --></p>
<p><!-- wp:paragraph --></p>
<p>When DSvClient runs, it traverses the URLs in sources.toml, intelligently downloading and organizing files to create this consistent structure. VIB files (VMware Installation Bundles) are stored in their appropriate vendor folders, while metadata files like XML indexes are placed at the proper hierarchy levels.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>The organization automatically matches what vCenter Update Manager expects, allowing you to point your internal web server directly at this folder structure without any additional configuration. This careful mirroring of VMware's repository structure is what makes DSvClient so effective for maintaining internal update repositories.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>The exclude section in config.toml lets you omit specific paths (like those "NEC" VIBs shown in the example configuration) to optimize storage space and download time, while still maintaining the overall repository structure.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading --></p>
<h2>DSvClient Decision Flow Chart</h2>
<p>
<!-- /wp:heading --></p>
<p>Here is a simplified visual representation of the applications decision flow</p>
<p>
<!-- wp:code --></p>
<pre class="wp-block-code"><code>┌─────────────────────────┐<restore-line-break>│ DSvClient Start         │<restore-line-break>└──────────────┬──────────┘<restore-line-break>               │<restore-line-break>               ▼<restore-line-break>┌─────────────────────────┐<restore-line-break>│ Parse Configuration     │<restore-line-break>│ - Read config.toml      │<restore-line-break>│ - Read sources.toml     │<restore-line-break>└──────────────┬──────────┘<restore-line-break>               │<restore-line-break>               ▼<restore-line-break>┌─────────────────────────┐<restore-line-break>│ Process Command Args    │<restore-line-break>└──────────────┬──────────┘<restore-line-break>               │<restore-line-break>           ┌───┴────┐<restore-line-break>           ▼        ▼<restore-line-break>┌────────────┐    ┌────────────┐<restore-line-break>│ Path Only  │    │ Path with  │<restore-line-break>│ (Download) │    │ --verify   │<restore-line-break>└─────┬──────┘    └──────┬─────┘<restore-line-break>      │                  │<restore-line-break>      ▼                  │<restore-line-break>┌────────────────────────┐<restore-line-break>│ Download Mode          │◄─────────────────┐<restore-line-break>├────────────────────────┤                  │<restore-line-break>│ For each source in     │                  │<restore-line-break>│ sources.toml:          │                  │<restore-line-break>└──────────┬─────────────┘                  │<restore-line-break>           │                                │<restore-line-break>           ▼                                │<restore-line-break>┌────────────────────────┐                  │<restore-line-break>│ Source Type Decision   │                  │<restore-line-break>└──────────┬─────────────┘                  │<restore-line-break>           │                                │<restore-line-break>     ┌─────┼────────┬─────────┬────────┐    │<restore-line-break>     ▼     ▼        ▼         ▼        ▼    │<restore-line-break>┌─────────┐ ┌───────┐ ┌───────┐ ┌──────────┐│<restore-line-break>│ ESXi    │ │ VCSA  │ │ vSAN  │ │ Add-ons  ││<restore-line-break>│ Updates │ │ Files │ │ HCL   │ │ &amp; Drivers││<restore-line-break>└────┬────┘ └───┬───┘ └───┬───┘ └────┬─────┘│<restore-line-break>     │          │         │          │      │<restore-line-break>     └──────────┴────┬────┴──────────┘      │<restore-line-break>                     │                       │<restore-line-break>                     ▼                       │<restore-line-break>┌────────────────────────┐                   │<restore-line-break>│ For each file:         │                   │<restore-line-break>└──────────┬─────────────┘                   │<restore-line-break>           │                                 │<restore-line-break>           ▼                                 │<restore-line-break>┌────────────────────────┐                   │<restore-line-break>│ File Exists?           │                   │<restore-line-break>└──────────┬─────────────┘                   │<restore-line-break>           │                                 │<restore-line-break>      ┌────┴─────┐                           │<restore-line-break>      ▼          ▼                           │<restore-line-break>┌──────────┐  ┌──────────┐                   │<restore-line-break>│   No     │  │   Yes    │                   │<restore-line-break>└────┬─────┘  └────┬─────┘                   │<restore-line-break>     │             │                         │<restore-line-break>     │             ▼                         │<restore-line-break>     │      ┌────────────────────┐           │<restore-line-break>     │      │ Verify Checksum    │           │<restore-line-break>     │      └────────┬───────────┘           │<restore-line-break>     │               │                       │<restore-line-break>     │        ┌──────┴─────┐                 │<restore-line-break>     │        ▼            ▼                 │<restore-line-break>     │  ┌──────────┐  ┌──────────┐           │<restore-line-break>     │  │ Valid    │  │ Invalid  │           │<restore-line-break>     │  └──────────┘  └────┬─────┘           │<restore-line-break>     │        │            │                 │<restore-line-break>     └────────┐            │                 │<restore-line-break>              │            │                 │<restore-line-break>              │            ▼                 │<restore-line-break>              │    ┌─────────────────────┐   │<restore-line-break>              │    │ Delete Invalid File │   │<restore-line-break>              │    └──────────┬──────────┘   │<restore-line-break>              │               │              │<restore-line-break>              └───────────────┘              │<restore-line-break>                              │              │<restore-line-break>                              ▼              │<restore-line-break>                     ┌─────────────────────┐ │<restore-line-break>                     │ Download File       │ │<restore-line-break>                     └──────────┬──────────┘ │<restore-line-break>                                │            │<restore-line-break>                                ▼            │<restore-line-break>                     ┌─────────────────────┐ │<restore-line-break>                     │ Add to Processed    │ │<restore-line-break>                     │ Files List          │ │<restore-line-break>                     └──────────┬──────────┘ │<restore-line-break>                                │            │<restore-line-break>                                └────────────┘<restore-line-break>                                │<restore-line-break>                                ▼<restore-line-break>           ┌─────────────────────────┐<restore-line-break>           │ --verify Flag Present?  │<restore-line-break>           └──────────┬──────────────┘<restore-line-break>                      │<restore-line-break>               ┌──────┴─────┐<restore-line-break>               ▼            ▼<restore-line-break>        ┌──────────┐  ┌──────────┐<restore-line-break>        │   No     │  │   Yes    │<restore-line-break>        └────┬─────┘  └────┬─────┘<restore-line-break>             │             │<restore-line-break>             │             ▼<restore-line-break>             │     ┌────────────────────┐<restore-line-break>             │     │ Verification Mode  │<restore-line-break>             │     ├────────────────────┤<restore-line-break>             │     │ For each file in   │<restore-line-break>             │     │ repository:        │<restore-line-break>             │     └────────┬───────────┘<restore-line-break>             │              │<restore-line-break>             │              ▼<restore-line-break>             │     ┌────────────────────┐<restore-line-break>             │     │ Verify Checksum    │<restore-line-break>             │     └────────┬───────────┘<restore-line-break>             │              │<restore-line-break>             │          ┌───┴────┐<restore-line-break>             │          ▼        ▼<restore-line-break>             │    ┌──────────┐ ┌──────────┐<restore-line-break>             │    │  Valid   │ │ Invalid  │<restore-line-break>             │    └─────┬────┘ └────┬─────┘<restore-line-break>             │          │           │<restore-line-break>             │          │           ▼<restore-line-break>             │          │    ┌─────────────────────┐<restore-line-break>             │          │    │ Report Mismatch     │<restore-line-break>             │          │    └─────────┬───────────┘<restore-line-break>             │          │              │<restore-line-break>             │          └──────────────┘<restore-line-break>             │                         │<restore-line-break>             └─────────────────────────┘<restore-line-break>                                      │<restore-line-break>                                      ▼<restore-line-break>                          ┌──────────────────────┐<restore-line-break>                          │ Exit with status     │<restore-line-break>                          │ code based on errors │<restore-line-break>                          └──────────────────────┘</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:heading {"level":3} --></p>
<h3>Decision Flow Explanation</h3>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>DSvClient follows a logical decision tree to determine what actions to take. Here's how the application makes decisions:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>1. Initial Configuration</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>The application starts by parsing two key configuration files:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list --></p>
<ul>
<li><strong>config.toml</strong> - Contains operational parameters like concurrent download limits, verification settings, and exclusion patterns</li>
<li><strong>sources.toml</strong> - Defines what content to download and from where (ESXi updates, VCSA files, vSAN HCL, vendor add-ons)</li>
</ul>
<p>
<!-- /wp:list --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>2. Command Argument Processing</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>DSvClient then processes the command-line arguments to determine the primary mode of operation:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list --></p>
<ul>
<li><strong>Path only</strong> (e.g., <code>DSvClient /path/to/repo</code>) - Default download mode</li>
<li><strong>Path with --verify flag</strong> (e.g., <code>DSvClient /path/to/repo --verify</code>) - Verification mode</li>
</ul>
<p>
<!-- /wp:list --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>3. Download Mode Logic</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>When in download mode, DSvClient:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list {"ordered":true} --></p>
<ol>
<li>Processes each source defined in sources.toml</li>
<li>For each source, determines the appropriate handler based on the content type:</p>
<ul>
<li>ESXi updates processor</li>
<li>VCSA files processor</li>
<li>vSAN HCL processor</li>
<li>Add-ons and drivers processor</li>
</ul>
</li>
<li>For each file within each source:</p>
<ul>
<li>Checks if the file already exists locally</li>
<li>If it exists, verifies its checksum</li>
<li>If the checksum is valid, skips the file</li>
<li>If the file doesn't exist or has an invalid checksum, downloads the file</li>
<li>Adds the file to the processed files list to avoid redundant operations</li>
</ul>
</li>
</ol>
<p>
<!-- /wp:list --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>4. Verification Mode Logic</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>When the --verify flag is provided (either directly or after download):</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list {"ordered":true} --></p>
<ol>
<li>The application scans all files in the repository</li>
<li>For each file, it verifies the checksum against expected values</li>
<li>Valid files are noted as passing verification</li>
<li>Invalid files are reported as mismatches</li>
<li>A summary report is generated showing verification results</li>
</ol>
<p>
<!-- /wp:list --></p>
<p><!-- wp:paragraph --></p>
<p>This decision flow ensures that DSvClient operates efficiently, avoiding unnecessary downloads while maintaining repository integrity. The semaphore-based concurrency controls (visible in the code) ensure that the application can process multiple files simultaneously without overwhelming system resources.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading --><!-- /wp:paragraph --></p>
<p><!-- wp:heading {"level":1} --></p>
<h1>Serving Internal Update Repositories with DSvClient</h1>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>A powerful extension to DSvClient's capabilities is setting up an internal web server to distribute your meticulously maintained repositories across your organization. This approach offers significant advantages for enterprises with multiple VMware environments or distributed teams.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading --></p>
<h2>Why Expose DSvClient Repositories via Web Server?</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>By serving your DSvClient repositories through a web server, you can:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list {"ordered":true} --></p>
<ol>
<li><strong>Create a centralized update source</strong> for all your environments</li>
<li><strong>Reduce external bandwidth consumption</strong> by downloading updates once for the entire organization</li>
<li><strong>Enforce update standardization</strong> across teams and departments</li>
<li><strong>Implement controlled update rollouts</strong> through repository staging</li>
<li><strong>Provide secure access</strong> to updates in restricted network segments</li>
</ol>
<p>
<!-- /wp:list --></p>
<p><!-- wp:heading --></p>
<h2>Implementation Options</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>Setting up a web server to host your DSvClient repository is straightforward. Here are three effective approaches:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading {"level":3} --></p>
<h3>1. Simple HTTP Server for Internal Networks</h3>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>For basic needs, a lightweight HTTP server works perfectly:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code># On Linux with Apache <restore-line-break>sudo mkdir -p /var/www/html/vmware-updates <restore-line-break>sudo ln -s /path/to/dsvclient-repository /var/www/html/vmware-updates <restore-line-break>sudo systemctl restart apache2 <restore-line-break><restore-line-break># On Windows with IIS <restore-line-break>New-WebVirtualDirectory -Site "Default Web Site" -Name "vmware-updates" -PhysicalPath "C:\VMware\UpdateRepo"</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:heading {"level":3} --></p>
<h3>2. Authenticated Access with NGINX</h3>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>For environments requiring user authentication:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code>server { listen 80; server_name updates.internal.example.com; location / { root /path/to/dsvclient-repository; autoindex on; auth_basic "VMware Update Repository"; auth_basic_user_file /etc/nginx/.htpasswd; } }</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:heading {"level":3} --></p>
<h3>3. Docker-Based Solution for Quick Deployment</h3>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>For portable, consistent deployments:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code>docker run -d \ --name vmware-update-repo \ --restart=always \ -p 8080:80 \ -v /path/to/dsvclient-repository:/usr/share/nginx/html:ro \ -e NGINX_ENTRYPOINT_QUIET_LOGS=1 \ nginx:alpine</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:heading --></p>
<h2>Implementation: Multi-Site Architecture</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>Implementing a hierarchical update distribution system using DSvClient:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>1. <strong>Headquarters</strong>: Maintains the master repository with DSvClient and serves it via HTTPS</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code># Weekly update <restore-line-break>job 0 2 * * 0 /usr/local/bin/dsvclient /opt/vmware/master-repo &gt;&gt; /var/log/dsvclient.log 2&gt;&amp;1</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:paragraph --></p>
<p>2. <strong>Regional Offices</strong>: Synchronize from headquarters using a combination of rsync and DSvClient verification</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:code --></p>
<pre class="wp-block-code"><code># Pull updates from HQ <restore-line-break>rsync -avz --delete https://hq-updates.example.com/vmware-updates/ /opt/vmware/regional-repo/ <restore-line-break># Verify integrity <br>/usr/local/bin/dsvclient /opt/vmware/regional-repo --verify</code></pre>
<p>
<!-- /wp:code --></p>
<p><!-- wp:paragraph --></p>
<p>3. <strong>Local Sites</strong>: Configure Update Manager to use their regional repository URL</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>This architecture reduced their external bandwidth usage by over 95% while ensuring consistent patch levels across all sites.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading --></p>
<h2>Integrating with vCenter Update Manager</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>The web-served repositories integrate seamlessly with vCenter:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list {"ordered":true} --></p>
<ol>
<li>In the vSphere Client, navigate to <strong>Update Manager</strong> &gt; <strong>Settings</strong></li>
<li>Select <strong>Download Settings</strong></li>
<li>Choose <strong>Use a shared repository</strong></li>
<li>Enter your internal URL: <code>http://updates.internal.example.com/vmware-updates/</code></li>
</ol>
<p>
<!-- /wp:list --></p>
<p><!-- wp:paragraph --></p>
<p>Now your Update Manager will source all patches and updates from your carefully maintained internal repository.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading --></p>
<h2>Security Considerations</h2>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>When serving update repositories internally:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:list {"ordered":true} --></p>
<ol>
<li><strong>Consider HTTPS</strong> for sensitive networks, using internal CA certificates</li>
<li><strong>Implement access controls</strong> based on network segmentation or authentication</li>
<li><strong>Validate checksums</strong> at each distribution tier with DSvClient's <code>--verify</code> option</li>
<li><strong>Maintain logs</strong> of repository access for compliance purposes</li>
<li><strong>Regularly scan</strong> repository content with security tools</li>
</ol>
<p>
<!-- /wp:list --></p>
<p><!-- wp:paragraph --></p>
<p>This approach provides a perfect balance of security and accessibility for your VMware updates.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>By leveraging DSvClient with an internal web server, you create a robust, efficient distribution system for VMware updates that minimizes external dependencies while maximizing control over your update process. The result is a more resilient, bandwidth-efficient infrastructure that maintains consistent standards across your entire VMware estate.</p>
<h3>Note on Broadcom download token</h3>
<p>I have not have time to test DSvClient with the new download system Broadcom has put in place.</p>
<p>As it is compatible with Update Manager without a special release, I'm expecting that the data flow remains the same.</p>
<p>So only a url change may be needed.</p>
<div>
<h3>Summary</h3>
<div>DSvClient represents a significant advancement in VMware update and repository management. By providing a fast, reliable way to download, verify, and maintain VMware component repositories, it addresses key challenges facing VMware administrators in modern environments:</div>
<ul>
<li>It simplifies offline repository management for security-conscious organizations</li>
<li>It ensures file integrity through comprehensive verification</li>
<li>It enables consistent environments through centralized update management</li>
<li>It optimizes bandwidth usage in distributed deployments</li>
<li>It integrates seamlessly with automation workflows</li>
</ul>
<div>As environments grow increasingly complex and update cadences accelerate, tools like DSvClient become essential components of a well-managed VMware infrastructure. Whether you're managing a small cluster or an enterprise-spanning deployment, DSvClient offers tangible benefits in efficiency, reliability, and consistency.</div>
<div>I encourage you to download DSvClient from <a href="https://codeberg.org/MichaelRyom/DSvClient">Codeberg</a> and experience these benefits firsthand. Your update management workflow will never be the same.</div>
<div>Have you tried DSvClient in your environment? I'd love to hear about your experiences in the comments below!</div>
</div>
<div> </div>
<div>

</div>



            ]]>
        </content>
    </entry>
    <entry>
        <title>VMUGDK Presentation - PowerShell: From Zero To Hero</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/vmugdk-presentation-powershell-from-zero-to-hero/"/>
        <id>https://MichaelRyom.dk/vmugdk-presentation-powershell-from-zero-to-hero/</id>

        <updated>2022-12-15T16:34:55+01:00</updated>
            <summary type="html">
                <![CDATA[
                    Hi all Did a presentation on Powershell yesterday and only got around halfway through, before I ran out of time. I have publish the entire&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p>Hi all</p>
<p>Did a presentation on Powershell yesterday and only got around halfway through, before I ran out of time.</p>
<p>I have publish the entire thing on github if anyone want a peak at what I talk or didn't. No powerpoint just powershell code.</p>
<p>The presentation is an attempt to start slow and end up where modules and functions gets built.</p>
<p><a href="https://github.com/MichaelRyom/VMUGDK-12-2022" target="_blank" rel="noopener noreferrer">https://github.com/MichaelRyom/VMUGDK-12-2022</a></p>

<p>Enjoy</p>

            ]]>
        </content>
    </entry>
    <entry>
        <title>Part 3: SuperMicro SuperServer E300-8D 2 years later</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/part-3-supermicro-superserver-e300-8d-2-years-later/"/>
        <id>https://MichaelRyom.dk/part-3-supermicro-superserver-e300-8d-2-years-later/</id>

        <updated>2019-08-05T21:37:25+02:00</updated>
            <summary type="html">
                <![CDATA[
                    TOC Part 1: The NUC killer: SuperMicro SuperServer E300-8D Part 2: SuperMicro SuperServer E300-8D deep dive Part 3: SuperMicro SuperServer E300-8D 2 years later So&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <h4>TOC</h4>
<p>Part 1: <a href="https://michaelryom.dk/the-nuc-killer-supermicro-superserver-e300-8d/" target="_blank" rel="noopener noreferrer">The NUC killer: SuperMicro SuperServer E300-8D</a><br>Part 2: <a href="https://michaelryom.dk/part-2-supermicro-superserver-e300-8d-deep-dive/" target="_blank" rel="noopener noreferrer">SuperMicro SuperServer E300-8D deep dive</a><br>Part 3: SuperMicro SuperServer E300-8D 2 years later</p>
<p>So it is more than two years since I posted my first blog about the E300-8D and why I bought SuperMicro over an Intel NUC. It's about time for an update and an upgrade of the home lab.</p>
<h3>How has it been</h3>
<p>Before I jump into all the new stuff. Let me look at how it has been the last two years with the SuperMicro server in the lab. It has been excellent, no issues at all I have flashed BIOS, with no hiccups. IPMI is a life saver, not because I have had issues, but it is just to nice to be able do something stupid network wise and still be able to log in to the hosts and troubleshoot the issue. Still my only complaint about the SuperMicro, is that I need to buy license to use all the IPMI features like flashing the BIOS the easy way, instead of needing to create an USB stick and plug it in. It the same for all server vendors like HPE and DELL. Never the less it is just another cash cow. One you always want to have.</p>
<p>
<!-- wp:paragraph --></p>
<h3>The upgrade</h3>
<p>There are a few things that I have changed. Lets start with a list of items that I have bought.</p>
<ul>
<li>6 x 32 GB Samsung DDR4 PC2666 REG (M393A4K40BB2-CTD)</li>
<li>2 x WD Blue 3D NAND SATA-SSD 2TB 6GB/s M.2 2280 (WDS200T2B0B)</li>
<li>Intel NUC NUC8i3BEK2 - WHAT!?!</li>
<li>Intel SSD Solid-State Drive 660p Series 512GB M.2 PCI Express 3.0 x4 (NVMe)<br>(SSDPEKNW512G8X1)</li>
<li>2 x 3PORT M.2 NGFF SSD CARD ADAPTER PCI EXPRESS 3.0 M.2 NGFF CARD<br>(PEXM2SAT32N1)</li>
<li>2 x RSC-RR1U-E8 RISERCARD 1HE | PASSIVE PCI-E X8 (RSC-RR1U-E8)</li>
</ul>
<p>Also bought Noctua fans and a Micro SD card which turned out to be mistakes.</p>

<h3>More memory</h3>
<p>The price for memory has been high since I bought the 32 GB memory there is in each of my E300-8D's. Therefore I have never found the cash to buy the 128 GB each E300-8D can handle, until now! Since I already had two sticks of memory from another vendor, I have group those together with the new Samsung memory and there has been no issues. This was one of my biggest concerns, but I took a leap of fate and I worked no issues. So the lab has 256 GB of memory and it is still no enough, someday, just someday I'll hope that will be the case until then this will have to do :)</p>
<h3>vSAN</h3>
<p>I needed to be able to run a vSAN cluster, without to much trouble, when upgrade time comes around and without having to rebuild the home lab. That made me do two thing.</p>
<p>1. Buy a Intel NUC, as the SuperMicro server is just too expensive. This will be my "management cluster", single node. With Intel 660p M.2 and 32 GB memory. All it does is host the vSAN witness node and vCenter. Giving me 256 GB for all the fun stuff. A NSX manager will most likely also find its way over there, but for now just two VMs.</p>
<p>2. Storage upgrade! I ran hybrid mode, with an SATA cable to an external HDD. Not optimal and slow. When I first bough the E300-8D's I also bough some ASUS PCIe M.2 adaptors, but they are just one or two millimeters to tall, to fit inside the case. So I dropped them. Had a chat with a Canadian company, but it was WAY to much trouble buying from them, asking me to fax stuff to them!!!</p>
<p>Finally I found the Startech PEXM2SAT32N1, which is a PCIe card the holds one PCIe M.2 NVMe and two SATA M.2 cards. Just perfect for my needs, only one problem, it was also to tall. Luckly SuperMicro has a riser for PCIe slot. Making this the perfect solution. I could get more fast storage in the E300-8D and I could also expand the storage if you should need to do so at a later time.</p>
<p>I already had Samsung 960 Pro as cache tier, so I looked a something like it, but I found the options to be too expensive. So I settled for the Western Digital 2 TB Blue Line M.2 card. This should give me some decent amount of storage when combined with vSAN.</p>

<p>Now for some pictures</p>
<h3>Intel NUC</h3>
<p>Not much to say here. I had bought a micro SD card I thought I could install ESXi on, but It would not detect it. So I used an USB stick instead. I did not install on the Intel 660p card as I would like as much space available for VMs, after all it is only 512 GB of Storage, so I will be gone fast.</p>
<figure class="alignnone size-medium wp-image-2064"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190526_063754-300x225.jpg"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190526_063754-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063754-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063754-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063754-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063754-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190526_063725-e1565030215970-300x225.jpg"><figure class="alignnone size-medium wp-image-2062"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190526_063725-e1565030215970-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063725-e1565030215970-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063725-e1565030215970-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063725-e1565030215970-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190526_063725-e1565030215970-300x225-lg.jpg 1024w"></figure></a></p>
<h3>Startech PEXM2SAT32N1</h3>
<p>This is a very cheap card, I paid less than 50 USD per card. Very cheap if you ask me, the riser, which I will come to in a moment, was less than 30 USD.</p>
<p>1 NVMe M.2 card which uses the PCIe port on the motherboard and two SATA M.2 cards with has to be connected to the motherboard via one SATA cable each. Power is served from the PCIe port.</p>
<figure class="alignnone size-medium wp-image-2054"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201723-300x225.jpg"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201723-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201723-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201723-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201723-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201723-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201929-300x225.jpg"><figure class="alignnone size-medium wp-image-2053"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201929-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201929-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201929-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201929-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201929-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201922-300x225.jpg"><figure class="alignnone size-medium wp-image-2052"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201922-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201922-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201922-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201922-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201922-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_202531-300x225.jpg"><figure class="alignnone size-medium wp-image-2051"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_202531-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_202531-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_202531-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_202531-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_202531-300x225-lg.jpg 1024w"></figure></a></p>
<p>Not much else to say all the bits are there including a small screwdriver, which is used to change the back plate of the card. The screwdriver does not work with the screws used to hold the M.2 card in place, which is a bit weird. Why ship a screwdriver with the package if it only solves one problem.</p>
<h3>RSC-RR1U-E8 RISERCARD 1HE | PASSIVE PCI-E X8 (RSC-RR1U-E8)</h3>
<p>I started out lazy. Did not want to put I all together to test if the StarTech card worked. So here you can see how the riser looks and fit if only half assembled.</p>
<figure class="alignnone size-medium wp-image-2059"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201612-300x225.jpg"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201612-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201612-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201612-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201612-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201612-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201643-300x225.jpg"><figure class="alignnone size-medium wp-image-2057"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201643-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201643-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201643-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201643-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201643-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201653-300x225.jpg"><figure class="alignnone size-medium wp-image-2056"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201653-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201653-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201653-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201653-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201653-300x225-lg.jpg 1024w"></figure></a></p>
<p>Note the use need to break parts of the riser to make it fit. I had to break even more of to make it fit inside the Riser card bracket, but on that in a moment.</p>
<h3>Riser card bracket</h3>
<p>This come with the E300-8D as default. First I mounted the riser card in the bracket and then I mounted the StarTech card with the SATA M.2 card. I have tested it with two SATA and one NVMe on card and one on the motherboard and ESXi detected them all :)</p>
<h3><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_091732-225x300.jpg"><figure class="alignnone size-medium wp-image-2066"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_091732-225x300.jpg" alt="" width="225" height="300"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091732-225x300-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091732-225x300-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091732-225x300-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091732-225x300-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094209-300x225.jpg"><figure class="alignnone size-medium wp-image-2071"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094209-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094209-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094209-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094209-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094209-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094455-225x300.jpg"><figure class="alignnone size-medium wp-image-2072"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094455-225x300.jpg" alt="" width="225" height="300"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094455-225x300-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094455-225x300-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094455-225x300-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094455-225x300-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094501-225x300.jpg"><figure class="alignnone size-medium wp-image-2073"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094501-225x300.jpg" alt="" width="225" height="300"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094501-225x300-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094501-225x300-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094501-225x300-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094501-225x300-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094737-300x225.jpg"><figure class="alignnone size-medium wp-image-2067"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094737-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094737-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094737-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094737-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094737-300x225-lg.jpg 1024w"></figure></a></h3>
<h3>SATA cabling</h3>
<p>The SATA cabling is a bit of a hassle to fit. A SATA cable with both ends being L shaped, I think would be better. I any case this works as well.</p>
<figure class="alignnone size-medium wp-image-2076"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094745-300x225.jpg"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094745-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094745-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094745-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094745-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094745-300x225-lg.jpg 1024w"></figure></a> <a href="https://michaelryom.dk/media/posts/127/responsive/IMG_20190608_094725-300x225-md.jpg"><figure class="alignnone size-medium wp-image-2075"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094725-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094725-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094725-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094725-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094725-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094710-300x225.jpg"><figure class="alignnone size-medium wp-image-2074"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_094710-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094710-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094710-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094710-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_094710-300x225-lg.jpg 1024w"></figure></a></p>
<h3>The only issue</h3>
<p>The SATADOM, which I use for ESXi, touches the Riser card bracket. Is this an issue, I do not know, but just to be on the safe side. I have moved the satadom to the other dedicated slot just next to it. So be warned if you want to have multiple satadom, you might need a sata cable or verify if this is an issue.</p>
<figure class="alignnone size-medium wp-image-2070"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_091919-225x300.jpg"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_091919-225x300.jpg" alt="" width="225" height="300"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091919-225x300-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091919-225x300-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091919-225x300-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091919-225x300-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_091914-225x300.jpg"><figure class="alignnone size-medium wp-image-2069"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_091914-225x300.jpg" alt="" width="225" height="300"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091914-225x300-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091914-225x300-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091914-225x300-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_091914-225x300-lg.jpg 1024w"></figure></a> </p>

<p>Lastly this is what 128 GB of memory looks like an small form factor!</p>
<figure class="alignnone size-medium wp-image-2060"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201446-300x225.jpg"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_201446-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201446-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201446-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201446-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_201446-300x225-lg.jpg 1024w"></figure></a></p>

<p>That is all for this time thanks for reading this far!</p>
<p>ps. the Noctua fans did not work out for multiple reasons.</p>
<p>I bought the wrong once - This is my mistake.</p>
<ol>
<li style="list-style-type: none;">
<ol>
<li>They are too small compared to the once ready in</li>
<li>I'm not a fan of the rubber fittings that came with fans. They broke to easily. I used strips instead.</li>
<li>They are way too low in RPMs - The E300-8D get hot with them in. Around 80-90 degrees Celsius with "normal" load. I have not tested with 100% load.</li>
</ol>
</li>
</ol>
<p>The first picture shows the new fan. Second mounted. Third The old once. Fourth old vs new.</p>
<figure class="alignnone size-medium wp-image-2078"><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_205624-225x300.jpg"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_205624-225x300.jpg" alt="" width="225" height="300"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205624-225x300-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205624-225x300-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205624-225x300-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205624-225x300-lg.jpg 1024w"></figure></a><a href="https://MichaelRyom.dk/media/posts/127/IMG_20190608_102752-300x225.jpg"><figure class="alignnone size-medium wp-image-2081"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_102752-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_102752-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_102752-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_102752-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_102752-300x225-lg.jpg 1024w"></figure></a> <a href="https://michaelryom.dk/media/posts/127/responsive/IMG_20190608_100209-300x225-md.jpg"><figure class="alignnone size-medium wp-image-2080"><img  src="https://MichaelRyom.dk/media/posts/127/IMG_20190608_100209-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_100209-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_100209-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_100209-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190608_100209-300x225-lg.jpg 1024w"></figure></a> <a href="https://MichaelRyom.dk/media/posts/127/IMG_20190525_205746-e1565033289369-300x225.jpg"><figure class="alignnone size-medium wp-image-2079"><img loading="lazy"  src="https://MichaelRyom.dk/media/posts/127/IMG_20190525_205746-e1565033289369-300x225.jpg" alt="" width="300" height="225"  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205746-e1565033289369-300x225-xs.jpg 300w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205746-e1565033289369-300x225-sm.jpg 480w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205746-e1565033289369-300x225-md.jpg 768w ,https://MichaelRyom.dk/media/posts/127/responsive/IMG_20190525_205746-e1565033289369-300x225-lg.jpg 1024w"></figure></a></p>
<p>
<!-- /wp:paragraph --></p>

            ]]>
        </content>
    </entry>
    <entry>
        <title>vRops 7.5 Active Directory authentication fails</title>
        <author>
            <name>Michael Ryom</name>
        </author>
        <link href="https://MichaelRyom.dk/vrops-75-active-directory-authentication-fails/"/>
        <id>https://MichaelRyom.dk/vrops-75-active-directory-authentication-fails/</id>

        <updated>2019-04-29T09:00:32+02:00</updated>
            <summary type="html">
                <![CDATA[
                    vRops 7.5 has been out little over two weeks now and from the looks of it, its an epic release with so many improvements and&hellip;
                ]]>
            </summary>
        <content type="html">
            <![CDATA[
                <p><!-- wp:paragraph --></p>
<p>vRops 7.5 has been out little over two weeks now and from the looks of it, its an epic release with so many improvements and a great deal of new and exciting features.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>I have upgraded a few environments with no problems at all and then last week yet another one got upgraded. As it does most times the upgraded went fine and the pre-upgrade bundle had no changes which would affect the customers environment. </p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>I then went to the login page, to try to login to vRops. Selected the Active Directory source, typed my credentials and hit the login button. "Incorrect user name/password". I tried a few times more, thinking I might have fat fingered the password or username, but no dice. I logged in as local admin in vRops and looked at the vRops version, it had upgraded to 7.5. Check authentication sources under administration-&gt;access, and compared it to the customers second environment, it is all an exact copy of the environment I had issues with, only difference was the other environment had not been upgraded yet, so it is still running vRops 7.0.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>Log Insight to the rescue</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>Lucky, I thought, they have Log Insight so that might help me troubleshoot me issue. I set the source to the vRops node and search for my username. </p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:image {"id":2031} --></p>
<figure class="wp-block-image"><figure class="wp-image-2031"><img loading="lazy" src="https://MichaelRyom.dk/media/posts/126/Image-010.png" alt=""  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/126/responsive/Image-010-xs.png 300w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-010-sm.png 480w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-010-md.png 768w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-010-lg.png 1024w"></figure></figure>
<p>
<!-- /wp:image --></p>
<p><!-- wp:paragraph --></p>
<p>It worked, but the messages were a bit cryptic, messages like:</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:quote --></p>
<blockquote class="wp-block-quote">
<p>com.vmware.vcops.auth.server.util.AuthUtils.searchUserByNameAndSourceId - Search for user by name: Michael and sourceId: 3262cfdb-e8f4-4f7d-af92-0d291ca8fd88 returned 0 entries. Must return only one entry. Something wrong</p>
</blockquote>
<p>
<!-- /wp:quote --></p>
<p><!-- wp:paragraph --></p>
<p>and </p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:quote --></p>
<blockquote class="wp-block-quote">
<p>com.vmware.vcops.platform.gemfire.GemfireFunction.invokeHandlerMethod - Failed to invoke the method 'UserAuthentication.authenticateUser' : InvalidCredentialsException: No user exists with userName: Michael and sourceId: 3262cfdb-e8f4-4f7d-af92-0d291ca8fd88</p>
</blockquote>
<p>
<!-- /wp:quote --></p>
<p><!-- wp:paragraph --></p>
<p>I looked at authentication sources and access control lots of times, before I noticed something, which I thought might be a clue. In access control the Active Directory users username is in User Principle Name (UPN) style format. Meaning username@domain.com was shown as the username, but since I had always logged in with just the username,  SAM Account Name style, I had only tested that way of logging in. Could this be why the logs stated it could not find my username?</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:image {"id":2032} --></p>
<figure class="wp-block-image"><figure class="wp-image-2032"><img loading="lazy" src="https://MichaelRyom.dk/media/posts/126/Image-011.png" alt=""  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/126/responsive/Image-011-xs.png 300w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-011-sm.png 480w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-011-md.png 768w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-011-lg.png 1024w"></figure></figure>
<p>
<!-- /wp:image --></p>
<p><!-- wp:paragraph --></p>
<p>I quickly logout of vRops and login with the UPN style naming convention. I works! So something has been changed. I reach out on twitter and slack, nobody has noticed any login issues after upgrading to vRops 7.5.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>The problem</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>I scratch my head. As I already have upgrade my homelab vRops and had not noticed anything in that either. I logon to my homelab to verify if I can replicate the issue. Login just works, I scratch me head somewhat harder. Then I notice the UPN suffix is different from the domain name to which the customer is using.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>Back into the homelab, logon to the domain controller, open domain and trusts, right click on the root and properties and add the UPN of vmware.com, what better UPN to test with.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:image {"id":2033} --></p>
<figure class="wp-block-image"><figure class="wp-image-2033"><img loading="lazy" src="https://MichaelRyom.dk/media/posts/126/Image-012.png" alt=""  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/126/responsive/Image-012-xs.png 300w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-012-sm.png 480w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-012-md.png 768w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-012-lg.png 1024w"></figure></figure>
<p>
<!-- /wp:image --></p>
<p><!-- wp:paragraph --></p>
<p>Then I go into users and computers, find my user, right click properties, account and change the UPN suffix to @vmware.com.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:image {"id":2034} --></p>
<figure class="wp-block-image"><figure class="wp-image-2034"><img loading="lazy" src="https://MichaelRyom.dk/media/posts/126/Image-013.png" alt=""  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/126/responsive/Image-013-xs.png 300w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-013-sm.png 480w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-013-md.png 768w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-013-lg.png 1024w"></figure></figure>
<p>
<!-- /wp:image --></p>
<p><!-- wp:paragraph --></p>
<p>The old login still works. So I go to access control and see the UPN is still the old one of MichaelRyomDK.root. Under authentication sources I click on the update icon, to synchronize the authentication source.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:image {"id":2035} --></p>
<figure class="wp-block-image"><figure class="wp-image-2035"><img loading="lazy" src="https://MichaelRyom.dk/media/posts/126/Image-014.png" alt=""  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/126/responsive/Image-014-xs.png 300w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-014-sm.png 480w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-014-md.png 768w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-014-lg.png 1024w"></figure></figure>
<p>
<!-- /wp:image --></p>
<p><!-- wp:paragraph --></p>
<p>This time under access control, the UPN suffix has changed to @vmware.com.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>So I once again logout and try to login again, SAM style meaning only using my username. Hurry, it failed to login! I successfully replicated the error.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>So while trying to troubleshoot this issue one of the things that struck me as a bit odd was that under authentication sources, if I edit my source and looked under details the common name is set to userPrincipleName. This by the way is default when you add a new authentication source. This could explain why I was force to use UPN style logins, but it does not explain why if I am part of the original domain name or UPN suffix, I can do without and login with just my username.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:image {"id":2036} --></p>
<figure class="wp-block-image"><figure class="wp-image-2036"><img loading="lazy" src="https://MichaelRyom.dk/media/posts/126/Image-015.png" alt=""  sizes="(min-width: 920px) 703px, (min-width: 700px) calc(82vw - 35px), calc(100vw - 81px)" srcset="https://MichaelRyom.dk/media/posts/126/responsive/Image-015-xs.png 300w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-015-sm.png 480w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-015-md.png 768w ,https://MichaelRyom.dk/media/posts/126/responsive/Image-015-lg.png 1024w"></figure></figure>
<p>
<!-- /wp:image --></p>
<p><!-- wp:paragraph --></p>
<p>I change the common name to samAccountName, hit ok and synced the authentication source again. Now the users under access control, showed up with no UPN suffix. I logout of vRops and back in with just my username. It worked!</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:heading {"level":4} --></p>
<h4>Conclusion</h4>
<p>
<!-- /wp:heading --></p>
<p><!-- wp:paragraph --></p>
<p>So it seems that there has been a change in the way vRops 7.5 authenticates it users, so if you are using multiple or non default UPN suffix's, what you might see after upgrading to vRops 7.5 is issues with authenticating users.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>The quick fix is to under authentication source change the common name to samAccountName.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>I have been in contact with <a rel="noreferrer noopener" aria-label="Sunny Dua (opens in a new tab)" href="" target="_blank">Sunny Dua</a> which is looking into the problem with vRops team.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>With that said this was the only issue I had when upgrading to vRops 7.5 and for now there is a quick workaround to get this working again. So now you are truly ready to upgrade to vRops 7.5.</p>
<p>
<!-- /wp:paragraph --></p>
<p><!-- wp:paragraph --></p>
<p>Thanks and take care.</p>
<p>
<!-- /wp:paragraph --></p>

            ]]>
        </content>
    </entry>
</feed>
