From 60c15b2145df977e813bd67f2ebb35959377b191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20van=20Die=C3=ABn?= Date: Tue, 14 Jul 2026 14:56:12 +0200 Subject: [PATCH 1/2] HostPrep.ps1 v4.4.0: offer to fix the ESXi hostname before cert regen generate-certificates derives the certificate CN from the host's own configured hostname, so a host that still calls itself localhost.localdomain keeps minting the wrong CN however often it is regenerated. The script already refused to waste a reboot on that, but it left the operator to go fix the hostname by hand and re-run. Add Repair-ESXiHostname: on a mismatch, prompt Y/N, set the hostname and domain from the host list via Set-VMHostNetwork, read them back to confirm the change stuck, then continue into regeneration -- which reboots the host anyway, so the rename costs no extra reboot. Declining keeps the previous behaviour (skip regen, flag CN mismatch in the report). Hosts are never renamed silently. Found while testing against a live host. (GitHub issue #8) Co-Authored-By: Claude Opus 4.8 (1M context) --- HostPrep.ps1 | 116 ++++++++++++++++++++++++++++++++++++++++++++++++--- README.md | 20 ++++++++- 2 files changed, 128 insertions(+), 8 deletions(-) diff --git a/HostPrep.ps1 b/HostPrep.ps1 index 959e09a..0310e7c 100644 --- a/HostPrep.ps1 +++ b/HostPrep.ps1 @@ -25,7 +25,10 @@ 7. Certificate regeneration -- check CN vs FQDN; if mismatched, enable SSH temporarily, run /sbin/generate-certificates via the built-in OpenSSH client, disable SSH, reboot, and wait for the host to return - online + online. If the host's own configured hostname disagrees with the host + list, offer to correct it first (Y/N per host) -- generate-certificates + derives the CN from that hostname, so regenerating without fixing it + would reproduce the same wrong CN 8. Password reset (optional) -- reset root password to a VCF 9 compliant value; always runs last so the existing credential is valid throughout @@ -167,7 +170,7 @@ .NOTES Script : HostPrep.ps1 - Version : 4.3.0 + Version : 4.4.0 Author : Paul van Dieen Blog : https://www.hollebollevsan.nl Date : 2026-07-14 @@ -340,6 +343,13 @@ install command, instead of erroring on every PowerCLI cmdlet and still prompting for the host list, NTP servers and root password before failing at Connect-VIServer. + 4.4.0 - Certificate regeneration no longer just skips a host whose + configured hostname disagrees with the host list: added + Repair-ESXiHostname, which prompts Y/N and sets the ESXi + hostname/domain from the host list via Set-VMHostNetwork, + verifies the change, then continues into regeneration. The regen + reboot covers the rename too. Declining keeps the old behaviour + (skip, flag CN mismatch). (GitHub issue #8) #> [CmdletBinding()] @@ -375,7 +385,7 @@ param ( $ScriptMeta = @{ Name = "HostPrep.ps1" - Version = "4.3.0" + Version = "4.4.0" Author = "Paul van Dieen" Blog = "https://www.hollebollevsan.nl" Date = "2026-07-14" @@ -1233,6 +1243,87 @@ function Get-ESXiStorageType { } } +function Repair-ESXiHostname { + <# + .SYNOPSIS + Offers to correct the ESXi host's configured hostname and domain so a + regenerated certificate will carry the right CN. + + .DESCRIPTION + /sbin/generate-certificates builds the certificate CN from the host's own + configured FQDN. Regenerating while that hostname is wrong just reproduces + the wrong CN and wastes a reboot, so the caller checks first and calls this + when the host disagrees with the host list. + + Prompts Y/N, then sets the hostname and domain from the FQDN in the host + list via Set-VMHostNetwork and re-reads them from the host to confirm the + change took. Renaming a host is never silent. + + The caller regenerates the certificate and reboots immediately after, so + the rename needs no reboot of its own. + + .PARAMETER VMHost + Expected FQDN of the ESXi host, as listed in the host list file. + + .PARAMETER VMHostObj + Connected VMHost object, used to re-read the network config afterwards. + + .PARAMETER HostNetwork + VMHostNetworkInfo for the host (from Get-VMHostNetwork). Set-VMHostNetwork + takes this object rather than a VMHost. + + .OUTPUTS + [bool] $true if the host's FQDN now matches $VMHost and regeneration + should proceed; $false if declined, not applicable, or the change failed. + #> + param( + [Parameter(Mandatory)][string]$VMHost, + [Parameter(Mandatory)]$VMHostObj, + [Parameter(Mandatory)]$HostNetwork + ) + + # A short name in the host list gives no domain to configure, so there is + # nothing sensible to set here. + if ($VMHost -notmatch '\.') { + Write-Log " '$VMHost' is not an FQDN, so no domain can be derived. Hostname fix skipped." -Level WARN + return $false + } + + $desiredShort = $VMHost.Split('.')[0] + $desiredDomain = ($VMHost -split '\.', 2)[1] + + $answer = (Read-Host " Fix the hostname on $VMHost so the certificate CN will match? [Y/N]").Trim().ToUpper() + if ($answer -ne 'Y') { + Write-Log " Hostname fix declined." -Level WARN + return $false + } + + try { + Write-Log " Setting hostname : $desiredShort" -Color DarkGray + Write-Log " Setting domain : $desiredDomain" -Color DarkGray + + Set-VMHostNetwork -Network $HostNetwork -HostName $desiredShort -DomainName $desiredDomain ` + -Confirm:$false -ErrorAction Stop | Out-Null + + # Read it back from the host rather than trusting the call to have stuck: + # a wrong CN is exactly the failure this is meant to prevent. + $updated = Get-VMHostNetwork -VMHost $VMHostObj + $updatedFqdn = "$($updated.HostName).$($updated.DomainName)".ToLower().TrimEnd('.') + + if ($updatedFqdn -eq $VMHost.ToLower()) { + Write-Log " Hostname updated to '$updatedFqdn'. Proceeding with certificate regeneration." -Color Green + return $true + } + + Write-Log " Hostname still reads '$updatedFqdn' after the change. Regeneration skipped." -Level WARN + return $false + + } catch { + Write-Log " Failed to set the hostname: $_" -Level WARN + return $false + } +} + function Get-OptionalAdvancedSettings { <# .SYNOPSIS @@ -2264,6 +2355,7 @@ foreach ($esxiHost in $targetEsxiHosts) { try { if ($DryRun) { Write-Log " [DRY RUN] Would check if certificate CN matches hostname." -Color DarkYellow + Write-Log " [DRY RUN] On a hostname mismatch, would offer to set the ESXi hostname/domain to '$esxiHost'." -Color DarkYellow Write-Log " [DRY RUN] Would regenerate host certificate if needed." -Color DarkYellow Write-Log " [DRY RUN] Would reboot host and wait for it to come back online." -Color DarkYellow $hostResult.CertRegen = "OK" @@ -2288,10 +2380,22 @@ foreach ($esxiHost in $targetEsxiHosts) { # hosts file before attempting regen -- generate-certificates uses # the host's own configured FQDN, so a hostname mismatch means # regen would produce the same wrong CN and the reboot is wasted. - $hostNet = Get-VMHostNetwork -VMHost $vmHostObj - $esxiFqdn = "$($hostNet.HostName).$($hostNet.DomainName)".ToLower().TrimEnd('.') - if ($esxiFqdn -ne $esxiHost.ToLower()) { + $hostNet = Get-VMHostNetwork -VMHost $vmHostObj + $esxiFqdn = "$($hostNet.HostName).$($hostNet.DomainName)".ToLower().TrimEnd('.') + $hostnameOk = ($esxiFqdn -eq $esxiHost.ToLower()) + + # Mismatch is fixable from here: offer to set the hostname from the + # host list, then regenerate. The regen reboots the host anyway, so + # the rename costs nothing extra. + if (-not $hostnameOk) { Write-Log " ESXi hostname '$esxiFqdn' does not match expected FQDN '$esxiHost'." -Level WARN + $hostnameOk = Repair-ESXiHostname ` + -VMHost $esxiHost ` + -VMHostObj $vmHostObj ` + -HostNetwork $hostNet + } + + if (-not $hostnameOk) { Write-Log " Certificate regeneration skipped: fix the ESXi hostname first or" -Level WARN Write-Log " the regenerated certificate will still have the wrong CN." -Level WARN $hostResult.CertRegen = "CN mismatch" diff --git a/README.md b/README.md index 1db3d8b..b95aef1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Two PowerShell scripts that automate ESXi host preparation and commissioning for | Script | Version | Purpose | |---|---|---| -| `HostPrep.ps1` | 4.3.0 | Prepares ESXi hosts — DNS, NTP, certificates, storage detection, disk wipe, advanced settings, password reset | +| `HostPrep.ps1` | 4.4.0 | Prepares ESXi hosts — DNS, NTP, certificates, storage detection, disk wipe, advanced settings, password reset | | `Commission-VCFHosts.ps1` | 3.1.4 | Commissions prepared hosts into SDDC Manager via the REST API | Run `HostPrep.ps1` first, then hand the generated CSV to `Commission-VCFHosts.ps1`. @@ -26,7 +26,7 @@ Reads a plain text file with one ESXi FQDN per line and runs the following steps 5. **Optional Advanced Settings** — applies any extra settings that are enabled, either in the script's built-in block or in an external settings file (see below) 6. **Storage type detection** — detects the primary storage type for the commissioning CSV (see below) 6b. **vSAN disk wipe** *(optional, `-WipeDisk` only)* — enumerates non-boot disks with existing partitions and wipes them via `partedUtil` over SSH, preparing disks for clean vSAN commissioning (see below) -7. **Certificate regeneration** — reads the TLS certificate from port 443 and checks whether the CN matches the host FQDN. If not: temporarily enables SSH, runs `/sbin/generate-certificates` via the built-in Windows OpenSSH client, disables SSH, reboots, waits for the host to return online +7. **Certificate regeneration** — reads the TLS certificate from port 443 and checks whether the CN matches the host FQDN. If not: temporarily enables SSH, runs `/sbin/generate-certificates` via the built-in Windows OpenSSH client, disables SSH, reboots, waits for the host to return online. If the host's own configured hostname disagrees with the host list (a fresh host still calling itself `localhost.localdomain`, say), the script offers to correct it first — see below 8. **Password reset** *(optional)* — resets the root password to a VCF 9 compliant value; always runs last so the existing credential stays valid throughout After all hosts are processed: @@ -62,6 +62,22 @@ Requires the Windows OpenSSH client (`ssh.exe`). Without it the script prints pe > **Boot disk is always protected.** It is excluded at enumeration time and never reaches `partedUtil` regardless of the Y/N answer. +### ESXi hostname mismatch + +`/sbin/generate-certificates` builds the certificate CN from the host's **own configured hostname**, not from whatever you put in the host list. So a freshly imaged host that still calls itself `localhost.localdomain` will keep minting `localhost.localdomain` certificates no matter how many times you regenerate — and each attempt costs a reboot. + +When the configured hostname disagrees with the host list, HostPrep offers to fix it: + +``` + ESXi hostname 'localhost.localdomain' does not match expected FQDN 'esxi01.vcf.lab'. + Fix the hostname on esxi01.vcf.lab so the certificate CN will match? [Y/N]: y + Setting hostname : esxi01 + Setting domain : vcf.lab + Hostname updated to 'esxi01.vcf.lab'. Proceeding with certificate regeneration. +``` + +**Y** sets the hostname and domain from the host list (`Set-VMHostNetwork`), reads them back to confirm, then continues into certificate regeneration — which reboots the host anyway, so the rename costs no extra reboot. **N** keeps the old behaviour: regeneration is skipped and the host is flagged `CN mismatch` in the report. Hosts are never renamed silently. + ### Storage type detection After connecting to each host, `HostPrep.ps1` detects the storage type and writes it to the commissioning CSV: From 38e4e7708d97a67d3e825746ff670beef7e5c16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20van=20Die=C3=ABn?= Date: Tue, 14 Jul 2026 15:02:43 +0200 Subject: [PATCH 2/2] HostPrep.ps1: detect DNS-from-DHCP before offering the hostname fix A host that obtains its DNS settings from DHCP takes its hostname and domain from there as well. Set-VMHostNetwork accepts -HostName/-DomainName on such a host, reports no error, and changes nothing -- so the read-back reported a baffling "hostname still reads 'localhost' after the change". Check DnsFromDhcp before prompting: explain that ESXi ignores a hostname set while DHCP is in charge, and point at the static-DNS setting. No pointless Y/N question, and no silent no-op call. Hit on a live host: the rename appeared to succeed and did nothing. Co-Authored-By: Claude Opus 4.8 (1M context) --- HostPrep.ps1 | 18 +++++++++++++++++- README.md | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/HostPrep.ps1 b/HostPrep.ps1 index 0310e7c..0883321 100644 --- a/HostPrep.ps1 +++ b/HostPrep.ps1 @@ -349,7 +349,10 @@ hostname/domain from the host list via Set-VMHostNetwork, verifies the change, then continues into regeneration. The regen reboot covers the rename too. Declining keeps the old behaviour - (skip, flag CN mismatch). (GitHub issue #8) + (skip, flag CN mismatch). Hosts taking DNS from DHCP are + detected up front: ESXi ignores a hostname set while DHCP is in + charge, so the script explains that and points at the static-DNS + setting instead of silently failing. (GitHub issue #8) #> [CmdletBinding()] @@ -1289,6 +1292,19 @@ function Repair-ESXiHostname { return $false } + # When the host takes its DNS settings from DHCP, it also takes its hostname and + # domain from there. Set-VMHostNetwork then accepts -HostName/-DomainName without + # error and silently changes nothing, so catch it here rather than letting the + # read-back below report a baffling "hostname unchanged". + if ($HostNetwork.DnsFromDhcp) { + Write-Log " This host obtains its DNS settings (and therefore its hostname and domain) from DHCP." -Level WARN + Write-Log " ESXi ignores a hostname set while DHCP is in charge, so the fix cannot be applied." -Level WARN + Write-Log " Set DNS to static on the host, then re-run -- in the ESXi UI:" -Level WARN + Write-Log " Networking > TCP/IP stacks > Default > Edit settings > 'Enter settings manually'" -Level WARN + Write-Log " A VCF host should not depend on DHCP for its identity in any case." -Level WARN + return $false + } + $desiredShort = $VMHost.Split('.')[0] $desiredDomain = ($VMHost -split '\.', 2)[1] diff --git a/README.md b/README.md index b95aef1..109e4c0 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ When the configured hostname disagrees with the host list, HostPrep offers to fi **Y** sets the hostname and domain from the host list (`Set-VMHostNetwork`), reads them back to confirm, then continues into certificate regeneration — which reboots the host anyway, so the rename costs no extra reboot. **N** keeps the old behaviour: regeneration is skipped and the host is flagged `CN mismatch` in the report. Hosts are never renamed silently. +> **Hosts on DHCP cannot be fixed this way.** If the host obtains its DNS settings from DHCP, it takes its hostname and domain from there too — ESXi accepts a hostname set via `Set-VMHostNetwork` and silently ignores it. The script detects this and tells you to switch the host to static DNS first (**Networking > TCP/IP stacks > Default > Edit settings > Enter settings manually**). A VCF host should not depend on DHCP for its identity in any case. + ### Storage type detection After connecting to each host, `HostPrep.ps1` detects the storage type and writes it to the commissioning CSV: