[{"content":"Modern infrastructure teams manage increasingly complex environments spanning vCenter, NSX, VCF, Aria Operations, backup platforms, firmware lifecycle tooling, storage, networking, and PowerCLI scripts. Operational questions often require specialist knowledge and multiple tools just to get a simple answer.\nThis post documents a working lab implementation that changes that — using Microsoft Copilot Studio as a conversational AI interface for VMware infrastructure.\nThe Problem Day-to-day infrastructure operations typically look like this:\nWhich workloads are running where? Is this cluster healthy? Are there operational risks right now? Do we have old snapshots? Are VMware Tools outdated? Which hosts need remediation? Each of these questions requires navigating multiple consoles, correlating information manually, and often depends on specialist knowledge. The result: slower troubleshooting, operational inconsistency, expert dependency, and knowledge silos.\nThe Solution: Intent-Centric Operations Generative AI creates a new operational interaction model. Instead of administrators navigating consoles and correlating data manually, they ask infrastructure questions conversationally:\n\u0026ldquo;How many VMs are running on this cluster?\u0026rdquo;\n\u0026ldquo;Which host is carrying the highest load?\u0026rdquo;\n\u0026ldquo;Do we have snapshots older than 30 days?\u0026rdquo;\n\u0026ldquo;Give me a health summary of this environment.\u0026rdquo;\nThis shifts operations from tool-centric to intent-centric.\nArchitecture Overview The implementation consists of five components working together:\nMicrosoft Copilot Studio — The conversational AI interface handling intent recognition, orchestration logic, tool selection, and response generation.\nCustom Connector — An OpenAPI integration layer providing action definitions, parameter mapping, and API abstraction. Key finding: Swagger 2.0 provided the most reliable compatibility — OpenAPI 3.x caused parsing and rendering issues in Copilot Studio.\nMicrosoft On-Premises Data Gateway — Provides secure hybrid connectivity between cloud AI services and local on-premises infrastructure.\nPython FastAPI Middleware — A custom-built operational API exposing VMware capabilities, handling authentication, REST endpoints, and VMware SDK integration.\nVMware pyVmomi SDK + vCenter — Direct VMware API access. vCenter is the system of operational truth.\nEnd-to-End Flow 1 2 3 4 5 6 7 8 9 User asks natural language question → Copilot interprets intent → Agent selects matching tool/action → Connector invokes backend API → Gateway securely proxies the request → FastAPI processes the call → pyVmomi queries VMware → Structured data returns → Copilot summarizes and reasons over results How We Built It Phase 1 — VMware API Layer A local Python FastAPI service was developed exposing these endpoints:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 /hosts - List all ESXi hosts /vms - List all VMs /vms/search - Search VMs by name /vms/poweredoff - List powered-off VMs /clusters - List clusters /clusters/summary - Cluster summary with stats /hosts/usage - Host CPU/memory usage /snapshots/old - Snapshots older than threshold /vmtools/outdated - VMs with outdated VMware Tools /events/recent - Recent vCenter events /tasks/recent - Recent tasks /vm/details - Detailed VM info /vm/storage - VM storage info /vm/snapshots - VM snapshot list Phase 2 — Validation Endpoints were tested locally before connecting to Copilot:\n1 Invoke-WebRequest http://localhost:8080/hosts Phase 3 — Hybrid Connectivity Installed and configured the Microsoft On-Premises Data Gateway with Azure identity, gateway registration, recovery key, and region settings.\nPhase 4 — Connector Integration Two important lessons learned:\nHost resolution: Using map:8080 as the host failed. localhost:8080 worked — because the gateway executes locally on the machine where it\u0026rsquo;s installed.\nOpenAPI compatibility: OpenAPI 3.x created parsing and rendering issues. Switching to Swagger 2.0 resolved all compatibility problems.\nPhase 5 — Agent Enablement Enabled connector tools inside Copilot Studio. The agent could then invoke live infrastructure actions conversationally.\nLive Demo Results The AI successfully handled inventory queries, capacity analysis, platform state checks, hygiene reporting (old snapshots, VMware Tools state), and AI-generated cluster health summaries.\nThe AI didn\u0026rsquo;t just return raw data — it reasoned about cluster distribution, identified imbalance, detected hygiene issues, and summarized health posture conversationally.\nHere\u0026rsquo;s what it looks like in Copilot Studio with live vCenter data:\nAdvisory Today → Actions Tomorrow The current prototype is read-only by design — no destructive actions are possible, enabling safe experimentation and operational trust building.\nPhase 2 will extend into governed write operations:\nVM Actions: power on/off, reboot, shutdown, snapshot create/delete Host Actions: maintenance mode, reboot, shutdown Mobility: vMotion, Storage vMotion Remediation: remove stale snapshots, VMware Tools remediation, lifecycle actions All write operations will require approval controls and role-based execution with confirm=true patterns and human validation.\nStrategic Expansion This architecture can extend beyond vCenter into:\nVMware Ecosystem: VCF / SDDC Manager, NSX, Aria Operations, Aria Automation, vSAN Health\nBroader Infrastructure: HPE iLO, Dell iDRAC, firmware lifecycle, storage platforms, backup systems\nEnterprise Integration: ServiceNow approval workflows, CMDB enrichment, incident remediation\nKey Takeaway This is not a theoretical AI concept. It is a working implementation showing that Microsoft Copilot Studio can operate as an intelligent control interface for VMware infrastructure.\nThe prototype already delivers meaningful operational value through conversational read operations. The next step is governed write automation — where AI doesn\u0026rsquo;t just answer questions, but takes controlled actions on infrastructure with appropriate human oversight.\nThe shift from tool-centric to intent-centric operations has started.\n","date":"2026-06-14T10:00:00+01:00","image":"/p/ai-driven-vmware-operations-with-microsoft-copilot-studio/cover.png","permalink":"/p/ai-driven-vmware-operations-with-microsoft-copilot-studio/","title":"AI-Driven VMware Operations with Microsoft Copilot Studio"},{"content":"Objective: Creating a complete nested pod from scratch containing ad/dc/dns, tftp, ntp and a complete VCF installation.\nstarting with automating the setup process in windows server in my new LAB.\nalready configured an ipblock in my external fw based on /16, and created vlan and interface in the virtual fw for my mgmt network.\nalready have a std windows 2022 server configured with static IP and a lokal admin.\nScript functionality\nThe script automates the initial configuration of a Windows server that will function as an Active Directory domain controller and DNS server. It performs the following configuration steps:\nConfigures the server hostname\nVerifies and disables Windows Defender Firewall\nDisables Internet Explorer Enhanced Security Configuration\nEnables Remote Desktop\nSets the system timezone\nConfigures time synchronization against a specified NTP source\nInstalls the AD DS and DNS Server roles\nCreates a new Active Directory domain\nCreates an administrative account and adds it to the Domain Admins group\nCreates additional standard user accounts\nCreates a reverse DNS lookup zone for the target subnet\nCreates both forward (A) and reverse (PTR) DNS records for the domain controller\nConfigures DNS forwarders\nCreates a baseline OU structure for administration and organization:\nServers\nUsers\nService Accounts\nEdit the configuration section to fit your needs.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 #requires -RunAsAdministrator param( [ValidateSet(\u0026#34;Stage0\u0026#34;,\u0026#34;Stage1\u0026#34;,\u0026#34;Stage2\u0026#34;)] [string]$Stage = \u0026#34;Stage0\u0026#34; ) $ErrorActionPreference = \u0026#34;Stop\u0026#34; $ComputerName = \u0026#34;addc01\u0026#34; $DomainName = \u0026#34;bervid.local\u0026#34; $NetBIOSName = \u0026#34;BERVID\u0026#34; $ServerIPv4 = \u0026#34;xx.3.xx.10\u0026#34; $PrefixLength = 24 $DefaultGateway = \u0026#34;xx.3.xx.1\u0026#34; $PreferredDNS = \u0026#34;127.0.0.1\u0026#34; $NtpServer = \u0026#34;ntp.uio.no\u0026#34; $ReverseNetworkId = \u0026#34;xx.3.xx.0/24\u0026#34; $ReverseZoneName = \u0026#34;xx.3.xx.in-addr.arpa\u0026#34; $DefaultUserPwd = \u0026#34;VMware123!\u0026#34; $SafeModePassword = \u0026#34;VMware123!\u0026#34; $UsersToCreate = @(\u0026#34;bervid\u0026#34;,\u0026#34;user1\u0026#34;,\u0026#34;user2\u0026#34;,\u0026#34;user3\u0026#34;) $DnsForwarders = @(\u0026#34;1.1.1.1\u0026#34;,\u0026#34;8.8.8.8\u0026#34;) $ScriptPath = $MyInvocation.MyCommand.Path $TranscriptPath = \u0026#34;C:\\Build-ADDS-Lab-DC01-v4.log\u0026#34; $TaskStage1 = \u0026#34;Build-ADDS-Lab-DC01-v4-Stage1\u0026#34; $TaskStage2 = \u0026#34;Build-ADDS-Lab-DC01-v4-Stage2\u0026#34; Start-Transcript -Path $TranscriptPath -Append | Out-Null if ($PSVersionTable.PSEdition -ne \u0026#39;Desktop\u0026#39;) { throw \u0026#34;Run this in Windows PowerShell 5.1 (powershell.exe), not pwsh.\u0026#34; } function Write-Step { param([string]$Message) Write-Host \u0026#34;\u0026#34;; Write-Host \u0026#34;=== $Message ===\u0026#34; -ForegroundColor Cyan } function Get-BaseDN { ($DomainName.Split(\u0026#34;.\u0026#34;) | ForEach-Object { \u0026#34;DC=$_\u0026#34; }) -join \u0026#34;,\u0026#34; } function Register-StartupTask { param([string]$TaskName,[string]$NextStage) $action = New-ScheduledTaskAction -Execute \u0026#34;PowerShell.exe\u0026#34; -Argument \u0026#34;-ExecutionPolicy Bypass -File `\u0026#34;$ScriptPath`\u0026#34; -Stage $NextStage\u0026#34; $trigger = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal -UserId \u0026#34;SYSTEM\u0026#34; -LogonType ServiceAccount -RunLevel Highest $task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal Register-ScheduledTask -TaskName $TaskName -InputObject $task -Force | Out-Null } function Remove-StartupTask { param([string]$TaskName) Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue } function Ensure-StaticIP { Write-Step \u0026#34;Checking IPv4 configuration\u0026#34; $adapter = Get-NetAdapter | Where-Object Status -eq \u0026#34;Up\u0026#34; | Sort-Object ifIndex | Select-Object -First 1 if (-not $adapter) { throw \u0026#34;No active network adapter found.\u0026#34; } $existing = Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -ne \u0026#34;127.0.0.1\u0026#34; -and $_.IPAddress -notlike \u0026#34;169.254*\u0026#34; } if (-not $existing -or ($existing | Select-Object -First 1).IPAddress -ne $ServerIPv4) { $existing | ForEach-Object { try { Remove-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress $_.IPAddress -Confirm:$false -ErrorAction Stop } catch {} } Get-NetRoute -InterfaceIndex $adapter.ifIndex -DestinationPrefix \u0026#34;0.0.0.0/0\u0026#34; -ErrorAction SilentlyContinue | Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue New-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress $ServerIPv4 -PrefixLength $PrefixLength -DefaultGateway $DefaultGateway | Out-Null } Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -ServerAddresses $PreferredDNS } function Disable-Firewall { Write-Step \u0026#34;Checking Windows Defender Firewall\u0026#34; if (Get-NetFirewallProfile | Where-Object Enabled -eq \u0026#34;True\u0026#34;) { Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled False } Get-NetFirewallProfile | Select-Object Name, Enabled | Format-Table -AutoSize } function Disable-IESecurity { Write-Step \u0026#34;Disabling IE Enhanced Security Configuration\u0026#34; $adminKey = \u0026#34;HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A7-37EF-4B3F-8CFC-4F3A74704073}\u0026#34; $userKey = \u0026#34;HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A8-37EF-4B3F-8CFC-4F3A74704073}\u0026#34; foreach ($key in @($adminKey, $userKey)) { if (Test-Path $key) { Set-ItemProperty -Path $key -Name \u0026#34;IsInstalled\u0026#34; -Value 0 -Type DWord } } } function Enable-RDP { Write-Step \u0026#34;Enabling Remote Desktop\u0026#34; Set-ItemProperty -Path \u0026#34;HKLM:\\System\\CurrentControlSet\\Control\\Terminal Server\u0026#34; -Name \u0026#34;fDenyTSConnections\u0026#34; -Value 0 Enable-NetFirewallRule -DisplayGroup \u0026#34;Remote Desktop\u0026#34; -ErrorAction SilentlyContinue | Out-Null } function Set-TimeConfig { Write-Step \u0026#34;Configuring time zone and NTP\u0026#34; tzutil /s \u0026#34;W. Europe Standard Time\u0026#34; w32tm /config /manualpeerlist:$NtpServer /syncfromflags:manual /reliable:yes /update | Out-Null Restart-Service w32time -Force Start-Sleep -Seconds 3 w32tm /resync /force | Out-Null } function Ensure-HostNameOrReboot { Write-Step \u0026#34;Setting hostname\u0026#34; if ($env:COMPUTERNAME -ne $ComputerName) { Rename-Computer -NewName $ComputerName -Force Remove-StartupTask -TaskName $TaskStage1 Remove-StartupTask -TaskName $TaskStage2 Register-StartupTask -TaskName $TaskStage1 -NextStage \u0026#34;Stage1\u0026#34; Restart-Computer -Force exit } } function Install-ADDSAndPromote { Write-Step \u0026#34;Installing AD DS and DNS\u0026#34; Import-Module ServerManager -ErrorAction Stop Install-WindowsFeature AD-Domain-Services, DNS -IncludeManagementTools Remove-StartupTask -TaskName $TaskStage2 Register-StartupTask -TaskName $TaskStage2 -NextStage \u0026#34;Stage2\u0026#34; Write-Step \u0026#34;Promoting server to new forest/domain\u0026#34; $smPwd = ConvertTo-SecureString $SafeModePassword -AsPlainText -Force Install-ADDSForest ` -DomainName $DomainName ` -DomainNetbiosName $NetBIOSName ` -InstallDNS ` -SafeModeAdministratorPassword $smPwd ` -Force ` -NoRebootOnCompletion:$false } function Wait-ForAD { Write-Step \u0026#34;Waiting for Active Directory services\u0026#34; $max = 30; $count = 0 do { Start-Sleep -Seconds 10 $count++ $adReady = Get-Service NTDS -ErrorAction SilentlyContinue } until (($adReady -and $adReady.Status -eq \u0026#34;Running\u0026#34;) -or $count -ge $max) if (-not $adReady -or $adReady.Status -ne \u0026#34;Running\u0026#34;) { throw \u0026#34;Active Directory services did not become ready in time.\u0026#34; } } function Ensure-OU { param([string]$Name,[string]$Path) $ouDn = \u0026#34;OU=$Name,$Path\u0026#34; if (-not (Get-ADOrganizationalUnit -LDAPFilter \u0026#34;(distinguishedName=$ouDn)\u0026#34; -ErrorAction SilentlyContinue)) { New-ADOrganizationalUnit -Name $Name -Path $Path -ProtectedFromAccidentalDeletion $false } } function Create-OUStructure { Write-Step \u0026#34;Creating OU structure\u0026#34; Import-Module ActiveDirectory $baseDN = Get-BaseDN Ensure-OU -Name \u0026#34;Servers\u0026#34; -Path $baseDN Ensure-OU -Name \u0026#34;Users\u0026#34; -Path $baseDN Ensure-OU -Name \u0026#34;Service Accounts\u0026#34; -Path $baseDN } function Create-DomainUsers { Write-Step \u0026#34;Creating additional domain users\u0026#34; Import-Module ActiveDirectory $securePwd = ConvertTo-SecureString $DefaultUserPwd -AsPlainText -Force $usersOU = \u0026#34;OU=Users,$((Get-BaseDN))\u0026#34; foreach ($u in $UsersToCreate) { $existing = Get-ADUser -Filter \u0026#34;SamAccountName -eq \u0026#39;$u\u0026#39;\u0026#34; -ErrorAction SilentlyContinue if (-not $existing) { New-ADUser ` -Name $u ` -SamAccountName $u ` -UserPrincipalName \u0026#34;$u@$DomainName\u0026#34; ` -Path $usersOU ` -AccountPassword $securePwd ` -Enabled $true ` -PasswordNeverExpires $true } } Add-ADGroupMember -Identity \u0026#34;Domain Admins\u0026#34; -Members \u0026#34;bervid\u0026#34; -ErrorAction SilentlyContinue } function Ensure-ReverseZone { Write-Step \u0026#34;Ensuring reverse lookup zone\u0026#34; Import-Module DnsServer $zone = Get-DnsServerZone -Name $ReverseZoneName -ErrorAction SilentlyContinue if (-not $zone) { Add-DnsServerPrimaryZone -NetworkId $ReverseNetworkId -ReplicationScope \u0026#34;Domain\u0026#34; } } function Ensure-DNSRecords { Write-Step \u0026#34;Ensuring A and PTR records for addc01\u0026#34; Import-Module DnsServer $aRecord = Get-DnsServerResourceRecord -ZoneName $DomainName -Name $ComputerName -RRType \u0026#34;A\u0026#34; -ErrorAction SilentlyContinue if (-not $aRecord) { Add-DnsServerResourceRecordA -ZoneName $DomainName -Name $ComputerName -IPv4Address $ServerIPv4 } $lastOctet = ($ServerIPv4 -split \u0026#39;\\.\u0026#39;)[-1] $existingPTR = Get-DnsServerResourceRecord -ZoneName $ReverseZoneName -Name $lastOctet -RRType PTR -ErrorAction SilentlyContinue if (-not $existingPTR) { Add-DnsServerResourceRecordPtr -ZoneName $ReverseZoneName -Name $lastOctet -PtrDomainName \u0026#34;$ComputerName.$DomainName\u0026#34; } } function Set-DNSForwarders { Write-Step \u0026#34;Configuring DNS forwarders\u0026#34; Import-Module DnsServer $current = (Get-DnsServerForwarder -ErrorAction SilentlyContinue).IPAddress.IPAddressToString if ((@($current) -join \u0026#34;,\u0026#34;) -ne (@($DnsForwarders) -join \u0026#34;,\u0026#34;)) { Set-DnsServerForwarder -IPAddress $DnsForwarders -UseRecurse $true } } function Add-CheckResult { param([string]$Name,[bool]$Passed,[string]$Detail) [PSCustomObject]@{ Check=$Name; Status=$(if($Passed){\u0026#34;PASS\u0026#34;}else{\u0026#34;FAIL\u0026#34;}); Detail=$Detail } } function Final-Checks { Write-Step \u0026#34;Final validation summary\u0026#34; Import-Module ActiveDirectory Import-Module DnsServer $results = @() $baseDN = Get-BaseDN $results += Add-CheckResult \u0026#34;Hostname\u0026#34; ($env:COMPUTERNAME -eq $ComputerName) $env:COMPUTERNAME try { $domain = Get-ADDomain $results += Add-CheckResult \u0026#34;Domain created\u0026#34; ($domain.DNSRoot -eq $DomainName) $domain.DNSRoot } catch { $results += Add-CheckResult \u0026#34;Domain created\u0026#34; $false $_.Exception.Message } foreach ($ou in @(\u0026#34;Servers\u0026#34;,\u0026#34;Users\u0026#34;,\u0026#34;Service Accounts\u0026#34;)) { $ouDn = \u0026#34;OU=$ou,$baseDN\u0026#34; $exists = Get-ADOrganizationalUnit -LDAPFilter \u0026#34;(distinguishedName=$ouDn)\u0026#34; -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;OU $ou\u0026#34; ([bool]$exists) $ouDn } foreach ($u in $UsersToCreate) { $user = Get-ADUser -Filter \u0026#34;SamAccountName -eq \u0026#39;$u\u0026#39;\u0026#34; -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;User $u\u0026#34; ([bool]$user) $(if ($user) { $user.UserPrincipalName } else { \u0026#34;Missing\u0026#34; }) } $bervidDA = Get-ADGroupMember \u0026#34;Domain Admins\u0026#34; -ErrorAction SilentlyContinue | Where-Object { $_.SamAccountName -eq \u0026#34;bervid\u0026#34; } $results += Add-CheckResult \u0026#34;bervid in Domain Admins\u0026#34; ([bool]$bervidDA) $(if ($bervidDA) { \u0026#34;Member\u0026#34; } else { \u0026#34;Missing\u0026#34; }) $rev = Get-DnsServerZone -Name $ReverseZoneName -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;Reverse zone\u0026#34; ([bool]$rev) $ReverseZoneName $aRecord = Get-DnsServerResourceRecord -ZoneName $DomainName -Name $ComputerName -RRType \u0026#34;A\u0026#34; -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;A record addc01\u0026#34; ([bool]$aRecord) \u0026#34;$ComputerName.$DomainName\u0026#34; $lastOctet = ($ServerIPv4 -split \u0026#39;\\.\u0026#39;)[-1] $ptr = Get-DnsServerResourceRecord -ZoneName $ReverseZoneName -Name $lastOctet -RRType PTR -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;PTR record addc01\u0026#34; ([bool]$ptr) $ServerIPv4 $currentForwarders = (Get-DnsServerForwarder -ErrorAction SilentlyContinue).IPAddress.IPAddressToString $forwardersOk = ((@($currentForwarders) -join \u0026#34;,\u0026#34;) -eq (@($DnsForwarders) -join \u0026#34;,\u0026#34;)) $results += Add-CheckResult \u0026#34;DNS forwarders\u0026#34; $forwardersOk (@($currentForwarders) -join \u0026#34;, \u0026#34;) $results | Format-Table -AutoSize $failCount = @($results | Where-Object Status -eq \u0026#34;FAIL\u0026#34;).Count if ($failCount -eq 0) { Write-Host \u0026#34;`nDC01 build summary: SUCCESS\u0026#34; -ForegroundColor Green } else { Write-Host \u0026#34;`nDC01 build summary: COMPLETED WITH ISSUES ($failCount failed checks)\u0026#34; -ForegroundColor Yellow } } switch ($Stage) { \u0026#34;Stage0\u0026#34; { Write-Step \u0026#34;Stage 0 starting\u0026#34; Ensure-StaticIP Disable-Firewall Disable-IESecurity Enable-RDP Set-TimeConfig Ensure-HostNameOrReboot Install-ADDSAndPromote } \u0026#34;Stage1\u0026#34; { Write-Step \u0026#34;Stage 1 starting\u0026#34; Remove-StartupTask -TaskName $TaskStage1 Ensure-StaticIP Disable-Firewall Disable-IESecurity Enable-RDP Set-TimeConfig Install-ADDSAndPromote } \u0026#34;Stage2\u0026#34; { Write-Step \u0026#34;Stage 2 starting\u0026#34; Remove-StartupTask -TaskName $TaskStage2 Wait-ForAD Create-OUStructure Create-DomainUsers Ensure-ReverseZone Ensure-DNSRecords Set-DNSForwarders Final-Checks } } Stop-Transcript | Out-Null How to run:\n1 2 Set-ExecutionPolicy Bypass -Scope Process -Force .\\Build-ADDS-Lab-DC01-v4.ps1 Server will promote to DC and restart. After reboot phase 2 will continue via scheduled task.\nTo run it i thought the easiest would be to copy directly via RDP, but since my pod was behind a virtual pfsense i had to create a rule to allow MS RDP 3389 both in physical and virtual pfsense. Then i had powershell in RDP and copied what i needed.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 RDP traffic flow example [Admin PC] 192.168.50.25 | | TCP 3389 v [Edge Firewall] Route: 172.20.0.0/16 -\u0026gt; 192.168.50.13 | | forwarded toward pod firewall v [Pod Firewall - WAN] 192.168.50.13 Rule needed here: Allow TCP 3389 Source: 192.168.50.0/24 Destination: 172.20.10.0/24 | | routed into pod management VLAN v [Pod Management Network] 172.20.10.0/24 VLAN 310 | v [Windows Server] dc01.example.local 172.20.10.10 RDP listening on TCP 3389 So lets run it:\nIt renames, needs a reboot before it can continue. In my first version it failed with an error because of the renaming needs a boot, so in that case, restart and run the script, it will just continue.\nAfter a reboot you should have these services up and running.\nYou could run a pre-check script to make sure everything is done before you start with addc02:\nrun it:\n1 2 Set-ExecutionPolicy Bypass -Scope Process -Force .\\PreCheck-ADDS-Lab.ps1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #requires -RunAsAdministrator $ErrorActionPreference = \u0026#34;Stop\u0026#34; $DomainName = \u0026#34;bervid.local\u0026#34; $ComputerName = \u0026#34;addc01\u0026#34; $ServerIPv4 = \u0026#34;xx.3.xx.10\u0026#34; $ReverseZoneName = \u0026#34;xx.3.xx.in-addr.arpa\u0026#34; $ExpectedUsers = @(\u0026#34;bervid\u0026#34;,\u0026#34;user1\u0026#34;,\u0026#34;user2\u0026#34;,\u0026#34;user3\u0026#34;) function Write-Step { param([string]$Message) Write-Host \u0026#34;\u0026#34; Write-Host \u0026#34;=== $Message ===\u0026#34; -ForegroundColor Cyan } function Get-BaseDN { $parts = $DomainName.Split(\u0026#34;.\u0026#34;) ($parts | ForEach-Object { \u0026#34;DC=$_\u0026#34; }) -join \u0026#34;,\u0026#34; } Import-Module ActiveDirectory Import-Module DnsServer $baseDN = Get-BaseDN Write-Step \u0026#34;Checking domain\u0026#34; Get-ADDomain | Select-Object DNSRoot, NetBIOSName | Format-Table -AutoSize Write-Step \u0026#34;Checking forward DNS\u0026#34; Resolve-DnsName \u0026#34;$ComputerName.$DomainName\u0026#34; | Format-Table -AutoSize Write-Step \u0026#34;Checking reverse DNS zone\u0026#34; $rev = Get-DnsServerZone -Name $ReverseZoneName -ErrorAction SilentlyContinue if ($rev) { Write-Host \u0026#34;Reverse zone exists: $ReverseZoneName\u0026#34; } else { Write-Warning \u0026#34;Reverse zone is missing: $ReverseZoneName\u0026#34; } Write-Step \u0026#34;Checking PTR for addc01\u0026#34; try { Resolve-DnsName $ServerIPv4 | Format-Table -AutoSize } catch { Write-Warning \u0026#34;PTR lookup failed for $ServerIPv4\u0026#34; } Write-Step \u0026#34;Checking OUs\u0026#34; $ous = @(\u0026#34;Servers\u0026#34;,\u0026#34;Users\u0026#34;,\u0026#34;Service Accounts\u0026#34;) foreach ($ou in $ous) { $ouDn = \u0026#34;OU=$ou,$baseDN\u0026#34; $exists = Get-ADOrganizationalUnit -LDAPFilter \u0026#34;(distinguishedName=$ouDn)\u0026#34; -ErrorAction SilentlyContinue if ($exists) { Write-Host \u0026#34;OU exists: $ouDn\u0026#34; } else { Write-Warning \u0026#34;OU missing: $ouDn\u0026#34; } } Write-Step \u0026#34;Checking users\u0026#34; foreach ($u in $ExpectedUsers) { $user = Get-ADUser -Filter \u0026#34;SamAccountName -eq \u0026#39;$u\u0026#39;\u0026#34; -ErrorAction SilentlyContinue if ($user) { Write-Host \u0026#34;User exists: $u\u0026#34; } else { Write-Warning \u0026#34;User missing: $u\u0026#34; } } Write-Step \u0026#34;Checking Domain Admins membership for bervid\u0026#34; $da = Get-ADGroupMember \u0026#34;Domain Admins\u0026#34; | Where-Object { $_.SamAccountName -eq \u0026#34;bervid\u0026#34; } if ($da) { Write-Host \u0026#34;bervid is a member of Domain Admins\u0026#34; } else { Write-Warning \u0026#34;bervid is NOT a member of Domain Admins\u0026#34; } This should verify if everything is done as expected.\nIf you still want to add users manually:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Import-Module ActiveDirectory $DomainName = \u0026#34;bervid.local\u0026#34; $BaseDN = \u0026#34;DC=bervid,DC=local\u0026#34; $UsersOU = \u0026#34;OU=Users,$BaseDN\u0026#34; $Password = ConvertTo-SecureString \u0026#34;VMware123!\u0026#34; -AsPlainText -Force $Users = @(\u0026#34;bervid\u0026#34;,\u0026#34;user1\u0026#34;,\u0026#34;user2\u0026#34;,\u0026#34;user3\u0026#34;) foreach ($u in $Users) { if (-not (Get-ADUser -Filter \u0026#34;SamAccountName -eq \u0026#39;$u\u0026#39;\u0026#34; -ErrorAction SilentlyContinue)) { New-ADUser ` -Name $u ` -SamAccountName $u ` -UserPrincipalName \u0026#34;$u@$DomainName\u0026#34; ` -Path $UsersOU ` -AccountPassword $Password ` -Enabled $true ` -PasswordNeverExpires $true Write-Host \u0026#34;Created user $u\u0026#34; } else { Write-Host \u0026#34;User $u already exists\u0026#34; } } Add-ADGroupMember -Identity \u0026#34;Domain Admins\u0026#34; -Members \u0026#34;bervid\u0026#34; -ErrorAction SilentlyContinue Write-Host \u0026#34;Added bervid to Domain Admins\u0026#34; Now we can focus on DC02. When we deploy this be sure to run the newsid / sysprep integrated in vCenter or manually on the vm to be sure: C:\\Windows\\System32\\Sysprep\\Sysprep.exe , make sure to check oobe \u0026amp; generalize. IMPORTANT.\nThen its the second script:\nconfigures the server with its static IP, gateway, DNS, timezone, NTP, RDP, and basic lab settings\nrenames the server to addc02 if needed and reboots\nverifies DNS and connectivity to the existing DC (addc01)\njoins the existing bervid.local domain\ninstalls AD DS and DNS\npromotes the server to an additional domain controller\nreboots again\nupdates DNS client settings so it uses itself and the first DC\nchecks reverse DNS, A/PTR records, forwarders, and basic AD replication health\nrun it:\n1 2 Set-ExecutionPolicy Bypass -Scope Process -Force .\\Build-ADDS-Lab-DC02-v3.ps1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 #requires -RunAsAdministrator param( [ValidateSet(\u0026#34;Stage0\u0026#34;,\u0026#34;Stage1\u0026#34;,\u0026#34;Stage2\u0026#34;,\u0026#34;Stage3\u0026#34;)] [string]$Stage = \u0026#34;Stage0\u0026#34; ) $ErrorActionPreference = \u0026#34;Stop\u0026#34; $ComputerName = \u0026#34;addc02\u0026#34; $DomainName = \u0026#34;bervid.local\u0026#34; $NetBIOSName = \u0026#34;BERVID\u0026#34; $ServerIPv4 = \u0026#34;xx.3.xx.11\u0026#34; $PrefixLength = 24 $DefaultGateway = \u0026#34;xx.3.xx.1\u0026#34; $InitialPreferredDNS = @(\u0026#34;xx.3.xx.10\u0026#34;) $PostPromotionDNS = @(\u0026#34;xx.3.xx.11\u0026#34;,\u0026#34;xx.3.xx.10\u0026#34;) $NtpServer = \u0026#34;ntp.uio.no\u0026#34; $ReverseNetworkId = \u0026#34;xx.3.xx.0/24\u0026#34; $ReverseZoneName = \u0026#34;xx.3.xx.in-addr.arpa\u0026#34; $ExistingDC = \u0026#34;addc01.bervid.local\u0026#34; $DomainAdminUser = \u0026#34;Administrator\u0026#34; $DomainAdminPassword = \u0026#34;VMware123!\u0026#34; $SafeModePassword = \u0026#34;VMware123!\u0026#34; $DnsForwarders = @(\u0026#34;1.1.1.1\u0026#34;,\u0026#34;8.8.8.8\u0026#34;) $SiteName = \u0026#34;Default-First-Site-Name\u0026#34; $ScriptPath = $MyInvocation.MyCommand.Path $TranscriptPath = \u0026#34;C:\\Build-ADDS-Lab-DC02-v3.log\u0026#34; $TaskStage1 = \u0026#34;Build-ADDS-Lab-DC02-v3-Stage1\u0026#34; $TaskStage2 = \u0026#34;Build-ADDS-Lab-DC02-v3-Stage2\u0026#34; $TaskStage3 = \u0026#34;Build-ADDS-Lab-DC02-v3-Stage3\u0026#34; Start-Transcript -Path $TranscriptPath -Append | Out-Null if ($PSVersionTable.PSEdition -ne \u0026#39;Desktop\u0026#39;) { throw \u0026#34;Run this in Windows PowerShell 5.1 (powershell.exe), not pwsh.\u0026#34; } function Write-Step { param([string]$Message) Write-Host \u0026#34;\u0026#34;; Write-Host \u0026#34;=== $Message ===\u0026#34; -ForegroundColor Cyan } function Register-StartupTask { param([string]$TaskName,[string]$NextStage) $action = New-ScheduledTaskAction -Execute \u0026#34;PowerShell.exe\u0026#34; -Argument \u0026#34;-ExecutionPolicy Bypass -File `\u0026#34;$ScriptPath`\u0026#34; -Stage $NextStage\u0026#34; $trigger = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal -UserId \u0026#34;SYSTEM\u0026#34; -LogonType ServiceAccount -RunLevel Highest $task = New-ScheduledTask -Action $action -Trigger $trigger -Principal $principal Register-ScheduledTask -TaskName $TaskName -InputObject $task -Force | Out-Null } function Remove-StartupTask { param([string]$TaskName) Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue } function Ensure-StaticIP { param([string[]]$DnsServers) Write-Step \u0026#34;Checking IPv4 configuration\u0026#34; $adapter = Get-NetAdapter | Where-Object Status -eq \u0026#34;Up\u0026#34; | Sort-Object ifIndex | Select-Object -First 1 if (-not $adapter) { throw \u0026#34;No active network adapter found.\u0026#34; } $existing = Get-NetIPAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -ne \u0026#34;127.0.0.1\u0026#34; -and $_.IPAddress -notlike \u0026#34;169.254*\u0026#34; } if (-not $existing -or ($existing | Select-Object -First 1).IPAddress -ne $ServerIPv4) { $existing | ForEach-Object { try { Remove-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress $_.IPAddress -Confirm:$false -ErrorAction Stop } catch {} } Get-NetRoute -InterfaceIndex $adapter.ifIndex -DestinationPrefix \u0026#34;0.0.0.0/0\u0026#34; -ErrorAction SilentlyContinue | Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue New-NetIPAddress -InterfaceIndex $adapter.ifIndex -IPAddress $ServerIPv4 -PrefixLength $PrefixLength -DefaultGateway $DefaultGateway | Out-Null } Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -ServerAddresses $DnsServers } function Disable-Firewall { Write-Step \u0026#34;Checking Windows Defender Firewall\u0026#34; if (Get-NetFirewallProfile | Where-Object Enabled -eq \u0026#34;True\u0026#34;) { Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled False } } function Disable-IESecurity { Write-Step \u0026#34;Disabling IE Enhanced Security Configuration\u0026#34; $adminKey = \u0026#34;HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A7-37EF-4B3F-8CFC-4F3A74704073}\u0026#34; $userKey = \u0026#34;HKLM:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{A509B1A8-37EF-4B3F-8CFC-4F3A74704073}\u0026#34; foreach ($key in @($adminKey, $userKey)) { if (Test-Path $key) { Set-ItemProperty -Path $key -Name \u0026#34;IsInstalled\u0026#34; -Value 0 -Type DWord } } } function Enable-RDP { Write-Step \u0026#34;Enabling Remote Desktop\u0026#34; Set-ItemProperty -Path \u0026#34;HKLM:\\System\\CurrentControlSet\\Control\\Terminal Server\u0026#34; -Name \u0026#34;fDenyTSConnections\u0026#34; -Value 0 Enable-NetFirewallRule -DisplayGroup \u0026#34;Remote Desktop\u0026#34; -ErrorAction SilentlyContinue | Out-Null } function Set-TimeConfig { Write-Step \u0026#34;Configuring time zone and NTP\u0026#34; tzutil /s \u0026#34;W. Europe Standard Time\u0026#34; w32tm /config /manualpeerlist:$NtpServer /syncfromflags:manual /update | Out-Null Restart-Service w32time -Force Start-Sleep -Seconds 3 w32tm /resync /force | Out-Null } function Ensure-HostNameOrReboot { Write-Step \u0026#34;Setting hostname\u0026#34; if ($env:COMPUTERNAME -ne $ComputerName) { Rename-Computer -NewName $ComputerName -Force Remove-StartupTask -TaskName $TaskStage1 Remove-StartupTask -TaskName $TaskStage2 Remove-StartupTask -TaskName $TaskStage3 Register-StartupTask -TaskName $TaskStage1 -NextStage \u0026#34;Stage1\u0026#34; Restart-Computer -Force exit } } function Wait-ForDNSAndDC { Write-Step \u0026#34;Waiting for DNS and existing domain controller\u0026#34; $max = 30; $count = 0 do { Start-Sleep -Seconds 10 $count++ $dnsOk = $false $dcOk = $false try { Resolve-DnsName $ExistingDC -ErrorAction Stop | Out-Null; $dnsOk = $true } catch {} try { nltest /dsgetdc:$DomainName | Out-Null; $dcOk = $true } catch {} } until (($dnsOk -and $dcOk) -or $count -ge $max) if (-not ($dnsOk -and $dcOk)) { throw \u0026#34;Could not verify DNS and DC reachability for $DomainName\u0026#34; } } function New-DomainCredential { $user = \u0026#34;$DomainAdminUser@$DomainName\u0026#34; $securePwd = ConvertTo-SecureString $DomainAdminPassword -AsPlainText -Force New-Object System.Management.Automation.PSCredential($user, $securePwd) } function Test-DomainCredential { param([System.Management.Automation.PSCredential]$Credential) Write-Step \u0026#34;Validating domain credentials\u0026#34; try { if (Get-PSDrive -Name \u0026#34;ADTEST\u0026#34; -ErrorAction SilentlyContinue) { Remove-PSDrive -Name \u0026#34;ADTEST\u0026#34; -ErrorAction SilentlyContinue } $null = New-PSDrive -Name \u0026#34;ADTEST\u0026#34; -PSProvider FileSystem -Root \u0026#34;\\\\$ExistingDC\\SYSVOL\u0026#34; -Credential $Credential -ErrorAction Stop Remove-PSDrive -Name \u0026#34;ADTEST\u0026#34; -ErrorAction SilentlyContinue } catch { throw \u0026#34;Domain credential validation failed against \\\\$ExistingDC\\SYSVOL\u0026#34; } } function Join-DomainAndReboot { Write-Step \u0026#34;Joining existing domain\u0026#34; $currentComputerSystem = Get-CimInstance Win32_ComputerSystem if ($currentComputerSystem.PartOfDomain -and $currentComputerSystem.Domain -ieq $DomainName) { Write-Host \u0026#34;Server is already joined to $DomainName\u0026#34; return } $cred = New-DomainCredential Test-DomainCredential -Credential $cred Remove-StartupTask -TaskName $TaskStage2 Register-StartupTask -TaskName $TaskStage2 -NextStage \u0026#34;Stage2\u0026#34; Add-Computer -DomainName $DomainName -Credential $cred -Force Restart-Computer -Force exit } function Install-ADDSAndPromote { Write-Step \u0026#34;Installing AD DS and DNS\u0026#34; Import-Module ServerManager -ErrorAction Stop Install-WindowsFeature AD-Domain-Services, DNS -IncludeManagementTools Remove-StartupTask -TaskName $TaskStage3 Register-StartupTask -TaskName $TaskStage3 -NextStage \u0026#34;Stage3\u0026#34; Write-Step \u0026#34;Promoting server to additional domain controller\u0026#34; $cred = New-DomainCredential $smPwd = ConvertTo-SecureString $SafeModePassword -AsPlainText -Force Install-ADDSDomainController ` -DomainName $DomainName ` -Credential $cred ` -InstallDns ` -SafeModeAdministratorPassword $smPwd ` -SiteName $SiteName ` -Force ` -NoRebootOnCompletion:$false } function Wait-ForAD { Write-Step \u0026#34;Waiting for Active Directory services\u0026#34; $max = 30; $count = 0 do { Start-Sleep -Seconds 10 $count++ $adReady = Get-Service NTDS -ErrorAction SilentlyContinue } until (($adReady -and $adReady.Status -eq \u0026#39;Running\u0026#39;) -or $count -ge $max) if (-not $adReady -or $adReady.Status -ne \u0026#39;Running\u0026#39;) { throw \u0026#34;Active Directory services did not become ready in time.\u0026#34; } } function Ensure-ReverseZone { Write-Step \u0026#34;Checking reverse lookup zone\u0026#34; Import-Module DnsServer $zone = Get-DnsServerZone -Name $ReverseZoneName -ErrorAction SilentlyContinue if (-not $zone) { Add-DnsServerPrimaryZone -NetworkId $ReverseNetworkId -ReplicationScope \u0026#34;Domain\u0026#34; } } function Ensure-DNSRecords { Write-Step \u0026#34;Ensuring A and PTR records for addc02\u0026#34; Import-Module DnsServer $aRecord = Get-DnsServerResourceRecord -ZoneName $DomainName -Name $ComputerName -RRType \u0026#34;A\u0026#34; -ErrorAction SilentlyContinue if (-not $aRecord) { Add-DnsServerResourceRecordA -ZoneName $DomainName -Name $ComputerName -IPv4Address $ServerIPv4 } $lastOctet = ($ServerIPv4 -split \u0026#39;\\.\u0026#39;)[-1] $existingPTR = Get-DnsServerResourceRecord -ZoneName $ReverseZoneName -Name $lastOctet -RRType PTR -ErrorAction SilentlyContinue if (-not $existingPTR) { Add-DnsServerResourceRecordPtr -ZoneName $ReverseZoneName -Name $lastOctet -PtrDomainName \u0026#34;$ComputerName.$DomainName\u0026#34; } } function Set-DNSForwarders { Write-Step \u0026#34;Configuring DNS forwarders\u0026#34; Import-Module DnsServer $current = (Get-DnsServerForwarder -ErrorAction SilentlyContinue).IPAddress.IPAddressToString if ((@($current) -join \u0026#34;,\u0026#34;) -ne (@($DnsForwarders) -join \u0026#34;,\u0026#34;)) { Set-DnsServerForwarder -IPAddress $DnsForwarders -UseRecurse $true } } function Set-PostPromotionDNSClient { Write-Step \u0026#34;Configuring post-promotion DNS client settings\u0026#34; $adapter = Get-NetAdapter | Where-Object Status -eq \u0026#34;Up\u0026#34; | Sort-Object ifIndex | Select-Object -First 1 if (-not $adapter) { throw \u0026#34;No active network adapter found.\u0026#34; } Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -ServerAddresses $PostPromotionDNS } function Add-CheckResult { param([string]$Name,[bool]$Passed,[string]$Detail) [PSCustomObject]@{ Check=$Name; Status=$(if($Passed){\u0026#34;PASS\u0026#34;}else{\u0026#34;FAIL\u0026#34;}); Detail=$Detail } } function Final-Checks { Write-Step \u0026#34;Final validation summary\u0026#34; Import-Module ActiveDirectory Import-Module DnsServer $results = @() $results += Add-CheckResult \u0026#34;Hostname\u0026#34; ($env:COMPUTERNAME -eq $ComputerName) $env:COMPUTERNAME try { $domain = Get-ADDomain $results += Add-CheckResult \u0026#34;Domain reachable\u0026#34; ($domain.DNSRoot -eq $DomainName) $domain.DNSRoot } catch { $results += Add-CheckResult \u0026#34;Domain reachable\u0026#34; $false $_.Exception.Message } $dcJoined = (Get-CimInstance Win32_ComputerSystem).PartOfDomain $results += Add-CheckResult \u0026#34;Domain joined\u0026#34; $dcJoined $((Get-CimInstance Win32_ComputerSystem).Domain) $aRecord = Get-DnsServerResourceRecord -ZoneName $DomainName -Name $ComputerName -RRType \u0026#34;A\u0026#34; -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;A record addc02\u0026#34; ([bool]$aRecord) \u0026#34;$ComputerName.$DomainName\u0026#34; $rev = Get-DnsServerZone -Name $ReverseZoneName -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;Reverse zone\u0026#34; ([bool]$rev) $ReverseZoneName $lastOctet = ($ServerIPv4 -split \u0026#39;\\.\u0026#39;)[-1] $ptr = Get-DnsServerResourceRecord -ZoneName $ReverseZoneName -Name $lastOctet -RRType PTR -ErrorAction SilentlyContinue $results += Add-CheckResult \u0026#34;PTR record addc02\u0026#34; ([bool]$ptr) $ServerIPv4 try { $repl = repadmin /replsummary 2\u0026gt;\u0026amp;1 | Out-String $results += Add-CheckResult \u0026#34;Replication summary ran\u0026#34; $true \u0026#34;repadmin /replsummary completed\u0026#34; } catch { $results += Add-CheckResult \u0026#34;Replication summary ran\u0026#34; $false $_.Exception.Message } $currentForwarders = (Get-DnsServerForwarder -ErrorAction SilentlyContinue).IPAddress.IPAddressToString $forwardersOk = ((@($currentForwarders) -join \u0026#34;,\u0026#34;) -eq (@($DnsForwarders) -join \u0026#34;,\u0026#34;)) $results += Add-CheckResult \u0026#34;DNS forwarders\u0026#34; $forwardersOk (@($currentForwarders) -join \u0026#34;, \u0026#34;) $results | Format-Table -AutoSize $failCount = @($results | Where-Object Status -eq \u0026#34;FAIL\u0026#34;).Count if ($failCount -eq 0) { Write-Host \u0026#34;`nDC02 build summary: SUCCESS\u0026#34; -ForegroundColor Green } else { Write-Host \u0026#34;`nDC02 build summary: COMPLETED WITH ISSUES ($failCount failed checks)\u0026#34; -ForegroundColor Yellow } } switch ($Stage) { \u0026#34;Stage0\u0026#34; { Write-Step \u0026#34;Stage 0 starting\u0026#34; Ensure-StaticIP -DnsServers $InitialPreferredDNS Disable-Firewall Disable-IESecurity Enable-RDP Set-TimeConfig Ensure-HostNameOrReboot Wait-ForDNSAndDC Join-DomainAndReboot } \u0026#34;Stage1\u0026#34; { Write-Step \u0026#34;Stage 1 starting\u0026#34; Remove-StartupTask -TaskName $TaskStage1 Ensure-StaticIP -DnsServers $InitialPreferredDNS Disable-Firewall Disable-IESecurity Enable-RDP Set-TimeConfig Wait-ForDNSAndDC Join-DomainAndReboot Install-ADDSAndPromote } \u0026#34;Stage2\u0026#34; { Write-Step \u0026#34;Stage 2 starting\u0026#34; Remove-StartupTask -TaskName $TaskStage2 Ensure-StaticIP -DnsServers $InitialPreferredDNS Disable-Firewall Disable-IESecurity Enable-RDP Set-TimeConfig Wait-ForDNSAndDC Install-ADDSAndPromote } \u0026#34;Stage3\u0026#34; { Write-Step \u0026#34;Stage 3 starting\u0026#34; Remove-StartupTask -TaskName $TaskStage3 Wait-ForAD Set-PostPromotionDNSClient Ensure-ReverseZone Ensure-DNSRecords Set-DNSForwarders Final-Checks } } Stop-Transcript | Out-Null If all goes well you should now be up and running with 2 synced DC/AD/DNS servers for your lab.\nThe last thing was DNS records.\ni was missing PTR record for my mgmt subnet, so i had to create that.\n1 Add-DnsServerPrimaryZone -NetworkId \u0026#34;10.3.10.0/24\u0026#34; -ReplicationScope \u0026#34;Domain\u0026#34; then i could run an import of all the different VCF services. I ran this on my addc01 server to easily prepare for the VCF install.\nThis creates an A record with PTR\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 Import-Module DnsServer $ZoneName = \u0026#34;lab.local\u0026#34; $ReverseZone = \u0026#34;10.168.192.in-addr.arpa\u0026#34; $Records = @( @{ Name = \u0026#34;mgmt01\u0026#34;; IP = \u0026#34;192.168.10.20\u0026#34; } @{ Name = \u0026#34;vc01\u0026#34;; IP = \u0026#34;192.168.10.21\u0026#34; } @{ Name = \u0026#34;host01\u0026#34;; IP = \u0026#34;192.168.10.22\u0026#34; } @{ Name = \u0026#34;host02\u0026#34;; IP = \u0026#34;192.168.10.23\u0026#34; } @{ Name = \u0026#34;host03\u0026#34;; IP = \u0026#34;192.168.10.24\u0026#34; } @{ Name = \u0026#34;host04\u0026#34;; IP = \u0026#34;192.168.10.25\u0026#34; } @{ Name = \u0026#34;host05\u0026#34;; IP = \u0026#34;192.168.10.26\u0026#34; } @{ Name = \u0026#34;host06\u0026#34;; IP = \u0026#34;192.168.10.27\u0026#34; } @{ Name = \u0026#34;host07\u0026#34;; IP = \u0026#34;192.168.10.28\u0026#34; } @{ Name = \u0026#34;host08\u0026#34;; IP = \u0026#34;192.168.10.29\u0026#34; } @{ Name = \u0026#34;ops01\u0026#34;; IP = \u0026#34;192.168.10.30\u0026#34; } @{ Name = \u0026#34;ops02\u0026#34;; IP = \u0026#34;192.168.10.31\u0026#34; } @{ Name = \u0026#34;ops03\u0026#34;; IP = \u0026#34;192.168.10.32\u0026#34; } @{ Name = \u0026#34;auto01\u0026#34;; IP = \u0026#34;192.168.10.33\u0026#34; } @{ Name = \u0026#34;log01\u0026#34;; IP = \u0026#34;192.168.10.34\u0026#34; } @{ Name = \u0026#34;net01\u0026#34;; IP = \u0026#34;192.168.10.35\u0026#34; } @{ Name = \u0026#34;repo01\u0026#34;; IP = \u0026#34;192.168.10.36\u0026#34; } @{ Name = \u0026#34;svc01\u0026#34;; IP = \u0026#34;192.168.10.37\u0026#34; } @{ Name = \u0026#34;idm01\u0026#34;; IP = \u0026#34;192.168.10.38\u0026#34; } @{ Name = \u0026#34;idm02\u0026#34;; IP = \u0026#34;192.168.10.39\u0026#34; } @{ Name = \u0026#34;idm03\u0026#34;; IP = \u0026#34;192.168.10.40\u0026#34; } @{ Name = \u0026#34;dns01\u0026#34;; IP = \u0026#34;192.168.10.41\u0026#34; } @{ Name = \u0026#34;dns02\u0026#34;; IP = \u0026#34;192.168.10.42\u0026#34; } @{ Name = \u0026#34;gw01\u0026#34;; IP = \u0026#34;192.168.10.43\u0026#34; } @{ Name = \u0026#34;gw02\u0026#34;; IP = \u0026#34;192.168.10.44\u0026#34; } @{ Name = \u0026#34;jump01\u0026#34;; IP = \u0026#34;192.168.10.45\u0026#34; } @{ Name = \u0026#34;backup01\u0026#34;; IP = \u0026#34;192.168.10.46\u0026#34; } @{ Name = \u0026#34;proxy01\u0026#34;; IP = \u0026#34;192.168.10.47\u0026#34; } @{ Name = \u0026#34;edge01\u0026#34;; IP = \u0026#34;192.168.10.48\u0026#34; } @{ Name = \u0026#34;edge02\u0026#34;; IP = \u0026#34;192.168.10.49\u0026#34; } ) foreach ($rec in $Records) { $existingA = Get-DnsServerResourceRecord -ZoneName $ZoneName -Name $rec.Name -RRType \u0026#34;A\u0026#34; -ErrorAction SilentlyContinue if (-not $existingA) { Add-DnsServerResourceRecordA ` -ZoneName $ZoneName ` -Name $rec.Name ` -IPv4Address $rec.IP Write-Host \u0026#34;Created A record: $($rec.Name).$ZoneName -\u0026gt; $($rec.IP)\u0026#34; } else { Write-Host \u0026#34;A record already exists: $($rec.Name).$ZoneName\u0026#34; } } foreach ($rec in $Records) { $lastOctet = ($rec.IP -split \u0026#39;\\.\u0026#39;)[-1] $fqdn = \u0026#34;$($rec.Name).$ZoneName\u0026#34; $existingPTR = Get-DnsServerResourceRecord -ZoneName $ReverseZone -Name $lastOctet -RRType \u0026#34;PTR\u0026#34; -ErrorAction SilentlyContinue if (-not $existingPTR) { Add-DnsServerResourceRecordPtr ` -ZoneName $ReverseZone ` -Name $lastOctet ` -PtrDomainName $fqdn Write-Host \u0026#34;Created PTR record: $($rec.IP) -\u0026gt; $fqdn\u0026#34; } else { Write-Host \u0026#34;PTR record already exists for: $($rec.IP)\u0026#34; } } Write-Host \u0026#34;\u0026#34; Write-Host \u0026#34;Verification:\u0026#34; -ForegroundColor Cyan foreach ($rec in $Records) { $fqdn = \u0026#34;$($rec.Name).$ZoneName\u0026#34; try { Resolve-DnsName $fqdn -ErrorAction Stop | Out-Null Write-Host \u0026#34;Resolved OK: $fqdn\u0026#34; } catch { Write-Host \u0026#34;Resolution failed: $fqdn\u0026#34; -ForegroundColor Yellow } } Ok , now we are ready for the next step, which is nested host-install.\n","date":"2026-03-28T02:59:05+01:00","image":"/p/automating-an-ad/dc/dns-server-in-a-vcf-pod/cover.png","permalink":"/p/automating-an-ad/dc/dns-server-in-a-vcf-pod/","title":"Automating an AD/DC/DNS server in a VCF Pod"},{"content":"Deploy a simple two-tier application\nIn the last post we prepared VKS, and we are now ready to try some more useful applications.\nLets start with a two-tier application with a web frontend and a db.\nFirst i check if i have a suitable namespace to deploy to.I dont`t, so i create a new one called demoapp.\n1 kubectl --context vcflab02:svc-tkg-domain-c10 create ns demoapp Next is the Harbor Pull-Secret:\n1 2 3 4 5 kubectl --context vcflab02:svc-tkg-domain-c10 -n demoapp create secret docker-registry harbor-tkg-pull \\ --docker-server=harbor.vcf.local \\ --docker-username=\u0026#39;robot$tkg+tkg-pull\u0026#39; \\ --docker-password=\u0026#39;YOUR_PASSWORD\u0026#39; \\ --docker-email=\u0026#39;unused@local\u0026#39; fill in your own user info that you created previous in Harbor and verify that your robot account can do both push and pull.\nThen patch your default service account:\n1 2 kubectl --context vcflab02:svc-tkg-domain-c10 -n demoapp patch serviceaccount default \\ --type=merge -p \u0026#39;{\u0026#34;imagePullSecrets\u0026#34;:[{\u0026#34;name\u0026#34;:\u0026#34;harbor-tkg-pull\u0026#34;}]}\u0026#39; and verify:\n1 2 kubectl --context vcflab02:svc-tkg-domain-c10 -n demoapp get secret kubectl --context vcflab02:svc-tkg-domain-c10 -n demoapp get sa default -o yaml Now we are ready to run our script demoapp.sh\ndeploy database\ncreate internal DB service\ninitialize sample data\ndeploy web app\ncreate LoadBalancer service for web app\nwait for LB IP\ncreate DNS record\ntesting in browser\nWith Harbor access and image pull already working, the next step is to deploy a more realistic application. In this example, I use a simple two-tier setup with phpMyAdmin as the web frontend and MariaDB as the backend database. The web tier is exposed through its own Kubernetes LoadBalancer service and external IP, while the database remains internal only through a ClusterIP service.\nThis is a useful pattern because it reflects how many real applications are structured: the frontend is published externally, while the database is kept private inside the cluster. At the end of the script, the assigned LoadBalancer IP is printed so a DNS record such as demoapp.vcf.local can be created afterwards.\nuses the base context vcflab02 instead of expecting vcflab02:demoapp\ndeploys to a dedicated namespace, for example demoapp\nuses an FQDN, for example demoapp.vcf.local\nwaits for an assigned LoadBalancer IP\nprints the DNS record you need to create at the end\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } warn(){ echo \u0026#34;[WARN] $*\u0026#34; \u0026gt;\u0026amp;2; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; command -v kubectl \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;kubectl not found. Run 02-base-tools.sh first.\u0026#34; command -v docker \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;docker not found. Docker is required for image mirroring.\u0026#34; # Force base context for this environment VCF_CONTEXT_NAME=\u0026#34;${VCF_CONTEXT_NAME:-vcflab03}\u0026#34; # Demo app settings DEMO_NAME=\u0026#34;${DEMO_NAME:-demoapp}\u0026#34; DEMO_NAMESPACE=\u0026#34;${DEMO_NAMESPACE:-demoapp}\u0026#34; DEMO_FQDN=\u0026#34;${DEMO_FQDN:-demoapp.vcf.local}\u0026#34; # Upstream images UPSTREAM_DB_IMAGE=\u0026#34;${UPSTREAM_DB_IMAGE:-mariadb:11.4}\u0026#34; UPSTREAM_WEB_IMAGE=\u0026#34;${UPSTREAM_WEB_IMAGE:-phpmyadmin:5-apache}\u0026#34; # Images in Harbor DEMO_DB_IMAGE=\u0026#34;${DEMO_DB_IMAGE:-${HARBOR_FQDN}/${HARBOR_PROJECT}/mariadb:11.4}\u0026#34; DEMO_WEB_IMAGE=\u0026#34;${DEMO_WEB_IMAGE:-${HARBOR_FQDN}/${HARBOR_PROJECT}/phpmyadmin:5-apache}\u0026#34; # Database settings DEMO_DB_NAME=\u0026#34;${DEMO_DB_NAME:-demoapp}\u0026#34; DEMO_DB_USER=\u0026#34;${DEMO_DB_USER:-demoapp}\u0026#34; DEMO_DB_PASSWORD=\u0026#34;${DEMO_DB_PASSWORD:-ChangeMe123!}\u0026#34; DEMO_DB_ROOT_PASSWORD=\u0026#34;${DEMO_DB_ROOT_PASSWORD:-ChangeRoot123!}\u0026#34; # Storage DEMO_DB_STORAGE_SIZE=\u0026#34;${DEMO_DB_STORAGE_SIZE:-2Gi}\u0026#34; DEMO_STORAGE_CLASS=\u0026#34;${DEMO_STORAGE_CLASS:-management-storage-policy-thin}\u0026#34; # Optional static LB IP. Leave empty for auto-assignment. DEMO_LB_IP=\u0026#34;${DEMO_LB_IP:-}\u0026#34; # Auto mirror required images into Harbor AUTO_PUSH_IMAGES=\u0026#34;${AUTO_PUSH_IMAGES:-true}\u0026#34; # Use base supervisor context ctx=\u0026#34;${VCF_CONTEXT_NAME}\u0026#34; ensure_namespace_exists() { kubectl --context \u0026#34;$ctx\u0026#34; get ns \u0026#34;$DEMO_NAMESPACE\u0026#34; \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 \\ || die \u0026#34;Namespace ${DEMO_NAMESPACE} not found. Create it first.\u0026#34; } show_input_values() { log \u0026#34;Using values:\u0026#34; echo \u0026#34; VCF_CONTEXT_NAME = ${VCF_CONTEXT_NAME}\u0026#34; echo \u0026#34; DEMO_NAME = ${DEMO_NAME}\u0026#34; echo \u0026#34; DEMO_NAMESPACE = ${DEMO_NAMESPACE}\u0026#34; echo \u0026#34; DEMO_FQDN = ${DEMO_FQDN}\u0026#34; echo \u0026#34; UPSTREAM_DB_IMAGE = ${UPSTREAM_DB_IMAGE}\u0026#34; echo \u0026#34; UPSTREAM_WEB_IMAGE = ${UPSTREAM_WEB_IMAGE}\u0026#34; echo \u0026#34; DEMO_DB_IMAGE = ${DEMO_DB_IMAGE}\u0026#34; echo \u0026#34; DEMO_WEB_IMAGE = ${DEMO_WEB_IMAGE}\u0026#34; echo \u0026#34; DEMO_DB_NAME = ${DEMO_DB_NAME}\u0026#34; echo \u0026#34; DEMO_DB_USER = ${DEMO_DB_USER}\u0026#34; echo \u0026#34; DEMO_DB_STORAGE_SIZE = ${DEMO_DB_STORAGE_SIZE}\u0026#34; echo \u0026#34; DEMO_STORAGE_CLASS = ${DEMO_STORAGE_CLASS}\u0026#34; echo \u0026#34; AUTO_PUSH_IMAGES = ${AUTO_PUSH_IMAGES}\u0026#34; if [[ -n \u0026#34;${DEMO_LB_IP}\u0026#34; ]]; then echo \u0026#34; DEMO_LB_IP = ${DEMO_LB_IP}\u0026#34; else echo \u0026#34; DEMO_LB_IP = \u0026#34; fi } docker_login_harbor() { log \u0026#34;Logging in to Harbor\u0026#34; echo \u0026#34;${HARBOR_ROBOT_PASSWORD}\u0026#34; | docker login \u0026#34;${HARBOR_FQDN}\u0026#34; -u \u0026#34;${HARBOR_ROBOT_USER}\u0026#34; --password-stdin \u0026gt;/dev/null } image_exists_in_harbor() { local image=\u0026#34;$1\u0026#34; docker manifest inspect \u0026#34;${image}\u0026#34; \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 } mirror_image_to_harbor() { local upstream_image=\u0026#34;$1\u0026#34; local harbor_image=\u0026#34;$2\u0026#34; if image_exists_in_harbor \u0026#34;${harbor_image}\u0026#34;; then log \u0026#34;Image already exists in Harbor: ${harbor_image}\u0026#34; return 0 fi log \u0026#34;Image not found in Harbor, mirroring ${upstream_image} -\u0026gt; ${harbor_image}\u0026#34; docker pull \u0026#34;${upstream_image}\u0026#34; docker tag \u0026#34;${upstream_image}\u0026#34; \u0026#34;${harbor_image}\u0026#34; docker push \u0026#34;${harbor_image}\u0026#34; } ensure_demo_images() { if [[ \u0026#34;${AUTO_PUSH_IMAGES}\u0026#34; != \u0026#34;true\u0026#34; ]]; then log \u0026#34;AUTO_PUSH_IMAGES=false, skipping automatic image mirroring\u0026#34; return fi docker_login_harbor mirror_image_to_harbor \u0026#34;${UPSTREAM_DB_IMAGE}\u0026#34; \u0026#34;${DEMO_DB_IMAGE}\u0026#34; mirror_image_to_harbor \u0026#34;${UPSTREAM_WEB_IMAGE}\u0026#34; \u0026#34;${DEMO_WEB_IMAGE}\u0026#34; } deploy_demoapp() { log \u0026#34;Deploying ${DEMO_NAME} into namespace ${DEMO_NAMESPACE}\u0026#34; if [[ -n \u0026#34;${DEMO_LB_IP}\u0026#34; ]]; then cat /dev/null || true } wait_for_demo_lb_ip() { log \u0026#34;Waiting for LoadBalancer IP for ${DEMO_NAME}-web-lb\u0026#34; local lb_ip=\u0026#34;\u0026#34; local max_tries=36 local i for i in $(seq 1 \u0026#34;$max_tries\u0026#34;); do lb_ip=\u0026#34;$(get_demo_lb_ip)\u0026#34; if [[ -n \u0026#34;${lb_ip}\u0026#34; ]]; then echo \u0026#34;${lb_ip}\u0026#34; return 0 fi sleep 5 done return 1 } show_status() { log \u0026#34;Current objects\u0026#34; kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$DEMO_NAMESPACE\u0026#34; get deploy,pod,svc,pvc | egrep \u0026#34;NAME|${DEMO_NAME}\u0026#34; || true } print_next_steps() { local lb_ip lb_ip=\u0026#34;$(wait_for_demo_lb_ip || true)\u0026#34; echo echo \u0026#34;========================================\u0026#34; echo \u0026#34;Demo application deployment completed\u0026#34; echo \u0026#34;========================================\u0026#34; if [[ -n \u0026#34;${lb_ip}\u0026#34; ]]; then echo \u0026#34;Assigned LoadBalancer IP : ${lb_ip}\u0026#34; echo \u0026#34;Requested FQDN : ${DEMO_FQDN}\u0026#34; echo echo \u0026#34;Create this DNS record:\u0026#34; echo \u0026#34; ${DEMO_FQDN} -\u0026gt; ${lb_ip}\u0026#34; echo echo \u0026#34;Then open:\u0026#34; echo \u0026#34; http://${lb_ip}\u0026#34; echo \u0026#34; http://${DEMO_FQDN}\u0026#34; echo echo \u0026#34;phpMyAdmin login details:\u0026#34; echo \u0026#34; Server : ${DEMO_NAME}-db\u0026#34; echo \u0026#34; Username : ${DEMO_DB_USER}\u0026#34; echo \u0026#34; Password : ${DEMO_DB_PASSWORD}\u0026#34; echo echo \u0026#34;Demo database:\u0026#34; echo \u0026#34; Database : ${DEMO_DB_NAME}\u0026#34; echo \u0026#34; Table : demo_messages\u0026#34; else warn \u0026#34;No LoadBalancer IP assigned yet.\u0026#34; echo echo \u0026#34;Check again with:\u0026#34; echo \u0026#34; kubectl --context \\\u0026#34;$ctx\\\u0026#34; -n \\\u0026#34;$DEMO_NAMESPACE\\\u0026#34; get svc ${DEMO_NAME}-web-lb -o wide\u0026#34; echo echo \u0026#34;Once the IP appears, create this DNS record:\u0026#34; echo \u0026#34; ${DEMO_FQDN} -\u0026gt; \u0026#34; fi } main() { ensure_namespace_exists show_input_values ensure_demo_images deploy_demoapp wait_for_rollouts show_status print_next_steps log \u0026#34;08 done.\u0026#34; } main \u0026#34;$@\u0026#34; you may need to adjust to your specified storage class:\nIt`s defined in the script like this:\nDEMO_STORAGE_CLASS=\u0026quot;${DEMO_STORAGE_CLASS:-management-storage-policy-thin}\u0026quot;\nI used \u0026ldquo;management-storage-policy-thin\u0026rdquo;\nIf something fails, you should do a cleanup and run it again, use this:\n1 2 3 4 5 kubectl --context vcflab02 -n demoapp delete deployment demoapp-db demoapp-web --ignore-not-found kubectl --context vcflab02 -n demoapp delete service demoapp-db demoapp-web-lb --ignore-not-found kubectl --context vcflab02 -n demoapp delete secret demoapp-db-secret --ignore-not-found kubectl --context vcflab02 -n demoapp delete configmap demoapp-db-init --ignore-not-found kubectl --context vcflab02 -n demoapp delete pvc demoapp-db-pvc --ignore-not-found If you want to override fqdn or storage class, then run:\n1 2 3 4 DEMO_NAMESPACE=\u0026#34;demoapp\u0026#34; \\ DEMO_FQDN=\u0026#34;demoapp.vcf.local\u0026#34; \\ DEMO_STORAGE_CLASS=\u0026#34;management-storage-policy-thin\u0026#34; \\ ./demoapp.sh otherwise, just run: ./demoapp.sh\nif all goes well, you should get something like this👇🏻 wich will give you the IP you need for the FQDN and the login details:\n========================================\nDemo application deployment completed\nAssigned LoadBalancer IP : [INFO] Waiting for LoadBalancer IP for demoapp-web-lb 10.31.x.x Requested FQDN : demoapp.vcf.local\nCreate this DNS record: demoapp.vcf.local -\u0026gt; [INFO] Waiting for LoadBalancer IP for demoapp-web-lb 10.31.x.x\nThen open: http://INFO Waiting for LoadBalancer IP for demoapp-web-lb 10.31.x.x http://demoapp.vcf.local\nphpMyAdmin login details: Server : demoapp-db Username : demoapp Password : ChangeMe123!\nDemo database: Database : demoapp Table : demo_messages [INFO] 08 done. root@sv01-mgmt02 [ ~/bootstrap ]#\nI add demoapp.vcf.local to my dns with the IP above.\nI use the login info and verify:\nand the test message \u0026quot; Hello from VKS and MariaDB\u0026quot;:\nThis is just an example, i hope you found it useful.\nMany services that traditionally ran on dedicated Linux or Windows virtual machines can now be delivered as containerized applications on platforms such as Kubernetes. Instead of deploying one VM per service, organizations often run one containerized service per function, which makes deployment and lifecycle management more flexible.\nThe most common examples include web and ingress services such as NGINX, Apache, Envoy, and Traefik; container registries such as Harbor; CI/CD platforms such as GitLab and Argo CD; identity and access services such as Keycloak; secrets management with HashiCorp Vault; databases such as PostgreSQL, MariaDB/MySQL, MongoDB, and Redis; and observability stacks such as Prometheus and Grafana.\nIn practice, the best candidates for moving from VMs to containers are web applications, APIs, internal portals, monitoring tools, CI/CD platforms, identity services, message brokers, and many modern databases. Traditional Windows infrastructure, legacy applications, GUI-dependent software, and some highly specialized or licensed systems are more likely to remain on dedicated virtual machines.\nA simple way to describe it is:\nMany Linux-based infrastructure and application services can now be replaced by containerized platforms and applications, while Windows-based legacy and infrastructure workloads are often modernized more gradually.\nNext post will be about configuring a two-tier application published from VCF Automation.\n","date":"2026-03-23T02:59:22+01:00","image":"/p/deploying-applications-in-vks/deploying-applications-in-vks.png","permalink":"/p/deploying-applications-in-vks/","title":"Deploying applications in VKS"},{"content":"In a previous post i configured a supervisor cluster with VPC, and now i am ready to configure the rest and have a mgmt vm i can use with all the right tools. I chose VMware Photon 5.0 that you can find on:\nhttps://github.com/vmware/photon/wiki/Downloading-Photon-OS\nIt`s an ova, so easy to add to your cluster, and this is what i did after deploying the ova:\nFirst phase: Harbor and Contour\nInstall Harbor first (the package itself in the Supervisor).\nFind the load balancer IP that Harbor/Contour is actually using.\nCreate the DNS A record (harbor.vcf.local -\u0026gt; ).\nAfter that, you can configure CA trust, run Docker login, push/pull images, create secrets, and so on.\nSecond phase: Sequence based on scripts\nConfigure static networking on the management VM (IP/GW/DNS/NTP).\nInstall the required tools (curl, jq, openssl, kubectl, docker, and vcf-cli).\nConnect to the Supervisor (vcf context create / kubeconfig context).\nInstall the Harbor package in the Supervisor, if it is not already installed.\nFind the load balancer IP (Contour Envoy EXTERNAL-IP).\nCreate the DNS A record for harbor.vcf.local.\nAlternatively, use a temporary /etc/hosts entry before DNS is in place.\nRetrieve the Harbor CA certificate (/api/v2.0/systeminfo/getcert) and add it to the CA trust store.\nRun Docker login and push a test image (for example busybox).\nCreate an imagePullSecret and patch the default service account.\nTest that a pod can pull an image from Harbor.\nI start with harbor and contour installation, and i need to download the files from Broadcom.\nEasiest way is to logon to the support portal, choose downloads, then search for Harbor and contour.\nThen you login in to your vCenter, choose supervisor management and services.\nHere you add your new services Harbor and Contour.\nAfter install you need to add the contents of the second file, the data values.\nfirst screen you choose the version\nNext you add the contents from the yaml file\nFor contour you can leave this default.\nNext is Harbor and you install it the same way, but the yaml file with the data values needs to be configured.\nhostname – the FQDN for Harbor, for example harbor.vcf.local\nharborAdminPassword – the Harbor admin password\nsecretKey – the internal encryption key used by Harbor\ndatabase.password – the password for the internal Harbor database\nstorageClass – set this if you need a specific storage class or storage policy\nenableNginxLoadBalancer – typically enabled for external access\nenableContourHttpProxy – typically disabled in this setup\nOptional secrets to review: core.secret, core.xsrfKey, jobservice.secret, registry.secret\nOk, this takes a short while and now we need to find Harbor ip and make a fqdn in my DNS. To do this i need kubectl so i installed the windows version of VCF-cli on my win-mgmt vm. Normally you go to the api server portal for this but\u0026hellip;., this also need fqdn, so first you need to log in to your vcenter and view your supervisor:\nKubernetes API server gives me the right IP, and i can add it to my DNS. i named it sv01.vcf.local. This is the same FQDN i defined in the supervisor installation.\nNow i can logon to the portal:\nFollow the instructions running them in a powershell, and you are ready to run kubectl commands. I first run these on my windows mgmt vm.\nI need to find Harbor ip without DNS so i run:\n1 kubectl --context vcflab02:svc-contour-domain-c10 -n svc-contour-domain-c10 get svc -o wide This shows you 2 lines with contour and one for envoy. In the envoy line , look for external IP, this is the harbor ip.\nUse that for fqdn for harbor.vcf.local in DNS and your harbor portal should be available. Here you login with admin and the password that you configured in the data values yaml file.\nNext is two different Harbor prereqs:\nA Harbor robot account should be created before running the script, because it provides the credentials used for automated registry access.\nSteps\nLog in to the Harbor web interface using an administrative account.\nOpen the Harbor project that will store the container images.\nGo to the Robot Accounts section for that project.\nCreate a new robot account.\nAssign the permissions required for the script and image operations.\nAt minimum, the account must be able to pull images.\nIf the script or preparation steps will push test images, it must also be allowed to push images.\nGenerate the robot account secret/password.\nCopy and save both:\nthe robot account username\nthe robot account password/secret\nStore these credentials securely, because they will be needed later for:\ndocker login\npushing test images\ncreating a Kubernetes imagePullSecret\nAt the end of this step, you should have a working Harbor robot account with the correct permissions for the target Harbor project, and the credentials should be available for the next steps.\nYou might stop here and just use powershell, but i found it more secure and easier to have a separate vm just for this.Since all the commands are CLI based it makes more sense to use a linux vm.\nThis next section in the post is all about the scripts to configure your Photon vm.\nNote: vcf-cli is a prerequisite for the current version of the scripts and must be staged in a separate directory before execution, i used /root/bootstrap/.\nFirst i deploy a new Photon vm. I start a remote console in vcenter and log in with root. Default password for root is changeme, so change this the first time you log in.\nNext is to set an ip on interface eth0 so i can reach the server with ssh.\nThen we create a directory for the scripts and vcf-cli.\nNow i can copy the vcf-cli tar file with scp to /root/bootstrap with powershell\nI do the same with all my scripts:\nNext is ssh login with putty.\nI cd to /root/bootstrap/ and edit the first script with my lab info.\nWhen the config script is done, you can start with 01.\nbefore you do, verify that all your scripts don`t have crlf errors, run this:\n1 2 sed -i \u0026#39;s/\\r$//\u0026#39; *.sh chmod +x *.sh This is the order for the scripts:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 cd /root/bootstrap sed -i \u0026#39;s/\\r$//\u0026#39; *.sh chmod +x *.sh ./01-network-hostname-ntp.sh # reboot optional ./02-base-tools.sh ./04-vcf-cli-install.sh ./03-supervisor-ca.sh # source /etc/profile.d/vcf-ca.sh # or open new shell ./05-vcf-context-and-plugins.sh ./06-harbor-and-test.sh 00-config.sh\nThis is where you change the settings for hostname, ip, mask, gw,dns and ntp for your lab environment.This is the base config, the other scripts will read from this.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #!/usr/bin/env bash # Edit ONLY this file. # Persistent working directory export BOOTSTRAP_DIR=\u0026#34;/root/bootstrap\u0026#34; # Host / network export HOSTNAME_FQDN=\u0026#34;sv01-mgmt01.vcf.local\u0026#34; export MGMT_IFACE=\u0026#34;eth0\u0026#34; export MGMT_IP=\u0026#34;10.0.x.x\u0026#34; export MGMT_PREFIX=\u0026#34;24\u0026#34; export MGMT_GW=\u0026#34;10.0.x.x\u0026#34; # DNS export DNS1=\u0026#34;10.0.x.x\u0026#34; export DNS2=\u0026#34;\u0026#34; export SEARCH_DOMAIN=\u0026#34;vcf.local\u0026#34; # NTP (space separated if multiple) export NTP_SERVERS=\u0026#34;10.0.x.x\u0026#34; # Binary install dir export BIN_DIR=\u0026#34;/usr/local/bin\u0026#34; # kubectl version: \u0026#34;stable\u0026#34; or pin, for example \u0026#34;v1.30.5\u0026#34; export KUBECTL_VERSION=\u0026#34;v1.30.5\u0026#34; # VCF CLI export VCF_TARBALL=\u0026#34;${BOOTSTRAP_DIR}/vcf-cli.tar.gz\u0026#34; export SUPERVISOR_ENDPOINT=\u0026#34;sv01.vcf.local\u0026#34; export VCF_CONTEXT_NAME=\u0026#34;vcflab01\u0026#34; export VCF_SSO_USER=\u0026#34;administrator@vsphere.local\u0026#34; # Harbor export HARBOR_FQDN=\u0026#34;harbor.vcf.local\u0026#34; export HARBOR_PROJECT=\u0026#34;tkg\u0026#34; export HARBOR_ROBOT_USER=\u0026#39;robot$tkg+tkg-pull\u0026#39; export HARBOR_ROBOT_PASSWORD=\u0026#34;CHANGE_ME\u0026#34; # Use temporary /etc/hosts entry if DNS is not ready export USE_ETC_HOSTS_FALLBACK=\u0026#34;false\u0026#34; export HARBOR_LB_IP=\u0026#34;10.x.x.x\u0026#34; # Kubernetes namespaces / contexts export NS_TKG=\u0026#34;svc-tkg-domain-c10\u0026#34; export NS_CONTOUR=\u0026#34;svc-contour-domain-c10\u0026#34; export NS_HARBOR=\u0026#34;svc-harbor-domain-c10\u0026#34; 01-network-hostname-ntp.sh\nThis is the network and ntp:\nPersists files in /root/bootstrap\nConfigures systemd-networkd static IP\nInstalls/configures chrony NTP\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; mkdir -p \u0026#34;$BOOTSTRAP_DIR\u0026#34; chmod 700 \u0026#34;$BOOTSTRAP_DIR\u0026#34; PHOTON_DHCP_FILE=\u0026#34;/etc/systemd/network/99-dhcp-en.network\u0026#34; STATIC_FILE=\u0026#34;/etc/systemd/network/99-static-${MGMT_IFACE}.network\u0026#34; log \u0026#34;Setting hostname to ${HOSTNAME_FQDN}\u0026#34; hostnamectl set-hostname \u0026#34;${HOSTNAME_FQDN}\u0026#34; if [[ -f \u0026#34;$PHOTON_DHCP_FILE\u0026#34; ]]; then ts=\u0026#34;$(date +%F_%H%M%S)\u0026#34; log \u0026#34;Archiving default DHCP file ${PHOTON_DHCP_FILE} -\u0026gt; ${PHOTON_DHCP_FILE}.bak.${ts}\u0026#34; mv -f \u0026#34;$PHOTON_DHCP_FILE\u0026#34; \u0026#34;${PHOTON_DHCP_FILE}.bak.${ts}\u0026#34; else log \u0026#34;No default DHCP file ${PHOTON_DHCP_FILE} found (ok).\u0026#34; fi shopt -s nullglob for f in /etc/systemd/network/*.network; do [[ \u0026#34;$f\u0026#34; == \u0026#34;$STATIC_FILE\u0026#34; ]] \u0026amp;\u0026amp; continue if grep -qE \u0026#34;^\\s*Name=${MGMT_IFACE}\\s*$\u0026#34; \u0026#34;$f\u0026#34; 2\u0026gt;/dev/null; then ts=\u0026#34;$(date +%F_%H%M%S)\u0026#34; log \u0026#34;Archiving conflicting file $f -\u0026gt; ${f}.bak.${ts}\u0026#34; mv -f \u0026#34;$f\u0026#34; \u0026#34;${f}.bak.${ts}\u0026#34; fi done shopt -u nullglob log \u0026#34;Writing static network config to ${STATIC_FILE}\u0026#34; { echo \u0026#34;[Match]\u0026#34; echo \u0026#34;Name=${MGMT_IFACE}\u0026#34; echo echo \u0026#34;[Network]\u0026#34; echo \u0026#34;Address=${MGMT_IP}/${MGMT_PREFIX}\u0026#34; echo \u0026#34;Gateway=${MGMT_GW}\u0026#34; echo \u0026#34;DNS=${DNS1}\u0026#34; [[ -n \u0026#34;$DNS2\u0026#34; ]] \u0026amp;\u0026amp; echo \u0026#34;DNS=${DNS2}\u0026#34; [[ -n \u0026#34;$SEARCH_DOMAIN\u0026#34; ]] \u0026amp;\u0026amp; echo \u0026#34;Domains=${SEARCH_DOMAIN}\u0026#34; } \u0026gt; \u0026#34;$STATIC_FILE\u0026#34; chmod 0644 \u0026#34;$STATIC_FILE\u0026#34; systemctl enable systemd-networkd \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true systemctl restart systemd-networkd || true sleep 2 ip -br a show \u0026#34;$MGMT_IFACE\u0026#34; || true ip r | sed -n \u0026#39;1,20p\u0026#39; || true log \u0026#34;Installing/configuring chrony\u0026#34; tdnf makecache -y \u0026gt;/dev/null || true tdnf install -y chrony \u0026gt;/dev/null || true if [[ -f /etc/chrony.conf ]]; then cp -a /etc/chrony.conf \u0026#34;/etc/chrony.conf.bak.$(date +%F_%H%M%S)\u0026#34; 2\u0026gt;/dev/null || true { echo \u0026#34;# Managed by 01-network-hostname-ntp.sh\u0026#34; for s in $NTP_SERVERS; do echo \u0026#34;server ${s} iburst\u0026#34; done echo \u0026#34;driftfile /var/lib/chrony/drift\u0026#34; echo \u0026#34;makestep 1.0 3\u0026#34; echo \u0026#34;rtcsync\u0026#34; } \u0026gt; /etc/chrony.conf systemctl enable chronyd \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true systemctl restart chronyd \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true fi chronyc sources -v || true log \u0026#34;01 done. Reboot is optional.\u0026#34; 02-base-tools.sh\nInstalls common tools you’ll need.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; log \u0026#34;Installing base tools\u0026#34; tdnf makecache -y \u0026gt;/dev/null || true tdnf install -y curl jq tar gzip unzip ca-certificates openssl docker \u0026gt;/dev/null || true log \u0026#34;Enabling docker\u0026#34; systemctl enable docker \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true systemctl start docker \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true if [[ \u0026#34;$KUBECTL_VERSION\u0026#34; == \u0026#34;stable\u0026#34; ]]; then ver=\u0026#34;$(curl -fsSL https://dl.k8s.io/release/stable.txt)\u0026#34; else ver=\u0026#34;$KUBECTL_VERSION\u0026#34; fi getent hosts dl.k8s.io \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;DNS cannot resolve dl.k8s.io. Fix DNS first.\u0026#34; mkdir -p \u0026#34;$BIN_DIR\u0026#34; url=\u0026#34;https://dl.k8s.io/release/${ver}/bin/linux/amd64/kubectl\u0026#34; log \u0026#34;Downloading kubectl ${ver} -\u0026gt; ${BIN_DIR}/kubectl\u0026#34; curl -fsSL \u0026#34;$url\u0026#34; -o \u0026#34;${BIN_DIR}/kubectl\u0026#34; chmod 0755 \u0026#34;${BIN_DIR}/kubectl\u0026#34; kubectl version --client || die \u0026#34;kubectl failed after install\u0026#34; docker version || true log \u0026#34;02 done.\u0026#34; 03-supervisor-ca.sh This step fixes the Supervisor x509 unknown authority in a Photon-minimal:\nExports SSL_CERT_FILE and CURL_CA_BUNDLE via /etc/profile.d/vcf-ca.sh\nIt extracts the server chain with openssl s_client\nAppends it to a custom bundle /etc/pki/tls/certs/ca-bundle.custom.crt\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; tdnf makecache -y \u0026gt;/dev/null || true tdnf install -y openssl ca-certificates curl \u0026gt;/dev/null || true CA_DIR=\u0026#34;${BOOTSTRAP_DIR}/certs\u0026#34; CUSTOM_BUNDLE=\u0026#34;/etc/pki/tls/certs/ca-bundle.custom.crt\u0026#34; PROFILED=\u0026#34;/etc/profile.d/vcf-ca.sh\u0026#34; mkdir -p \u0026#34;$CA_DIR\u0026#34; log \u0026#34;Fetching TLS chain from ${SUPERVISOR_ENDPOINT}:443\u0026#34; openssl s_client -showcerts -connect \u0026#34;${SUPERVISOR_ENDPOINT}:443\u0026#34; -servername \u0026#34;${SUPERVISOR_ENDPOINT}\u0026#34; /dev/null \\ | awk \u0026#39; /BEGIN CERTIFICATE/ {i++; fn=sprintf(\u0026#34;\u0026#39;\u0026#34;${CA_DIR}\u0026#34;\u0026#39;/cert-%d.crt\u0026#34;, i)} {if (i\u0026gt;0) print \u0026gt; fn} \u0026#39; ls -l \u0026#34;${CA_DIR}\u0026#34;/cert-*.crt \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;No certificates extracted from Supervisor.\u0026#34; log \u0026#34;Building custom CA bundle at ${CUSTOM_BUNDLE}\u0026#34; if [[ -f /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem ]]; then cat /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem \u0026#34;${CA_DIR}\u0026#34;/cert-*.crt \u0026gt; \u0026#34;$CUSTOM_BUNDLE\u0026#34; elif [[ -f /etc/pki/tls/certs/ca-bundle.crt ]]; then cat /etc/pki/tls/certs/ca-bundle.crt \u0026#34;${CA_DIR}\u0026#34;/cert-*.crt \u0026gt; \u0026#34;$CUSTOM_BUNDLE\u0026#34; else die \u0026#34;No base CA bundle found on system.\u0026#34; fi chmod 0644 \u0026#34;$CUSTOM_BUNDLE\u0026#34; cat \u0026gt; \u0026#34;$PROFILED\u0026#34; /dev/null || die \u0026#34;Supervisor TLS verification still failing.\u0026#34; log \u0026#34;03 done. Open a new shell or run: source ${PROFILED}\u0026#34; 04-vcf-cli-install.sh\nInstalls vcf-cli and puts it in /usr/local/bin.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; tdnf makecache -y \u0026gt;/dev/null || true tdnf install -y tar gzip ca-certificates curl \u0026gt;/dev/null || true if command -v vcf \u0026gt;/dev/null 2\u0026gt;\u0026amp;1; then log \u0026#34;vcf already installed: $(command -v vcf)\u0026#34; exit 0 fi [[ -f \u0026#34;$VCF_TARBALL\u0026#34; ]] || die \u0026#34;Missing $VCF_TARBALL\u0026#34; tmpdir=\u0026#34;$(mktemp -d /tmp/vcfcli.XXXXXX)\u0026#34; tar -xzf \u0026#34;$VCF_TARBALL\u0026#34; -C \u0026#34;$tmpdir\u0026#34; candidate=\u0026#34;\u0026#34; if [[ -f \u0026#34;${tmpdir}/vcf-cli-linux_amd64\u0026#34; ]]; then candidate=\u0026#34;${tmpdir}/vcf-cli-linux_amd64\u0026#34; else candidate=\u0026#34;$(find \u0026#34;$tmpdir\u0026#34; -maxdepth 2 -type f -name \u0026#34;vcf*\u0026#34; | head -n 1 || true)\u0026#34; fi [[ -n \u0026#34;$candidate\u0026#34; ]] || die \u0026#34;Could not locate vcf binary inside tarball.\u0026#34; install -m 0755 \u0026#34;$candidate\u0026#34; \u0026#34;${BIN_DIR}/vcf\u0026#34; rm -rf \u0026#34;$tmpdir\u0026#34; log \u0026#34;Installed vcf -\u0026gt; ${BIN_DIR}/vcf\u0026#34; vcf version || true log \u0026#34;04 done.\u0026#34; 05-vcf-context-and-plugins.sh (manual login step) This step is intentionally interactive (because automating the prompt has been flaky and caused hangs/crashes).\nIt will:\nactivate context to trigger plugin installation\nverify Supervisor TLS works\nrun the exact commands manually\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } command -v vcf \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;vcf not found. Run 04-vcf-cli-install.sh first.\u0026#34; if [[ -f /etc/profile.d/vcf-ca.sh ]]; then # shellcheck disable=SC1091 source /etc/profile.d/vcf-ca.sh fi log \u0026#34;Testing Supervisor TLS before context creation\u0026#34; curl -fsS \u0026#34;https://${SUPERVISOR_ENDPOINT}/\u0026#34; \u0026gt;/dev/null || die \u0026#34;Supervisor TLS still failing. Run 03-supervisor-ca.sh first.\u0026#34; cat Then run: vcf context use ${VCF_CONTEXT_NAME} That step installs recommended plugins. Verify with: vcf context list --wide EOF read -rp \u0026#34;Press ENTER when you are ready to run \u0026#39;vcf context create\u0026#39;...\u0026#34; vcf context create --endpoint=\u0026#34;${SUPERVISOR_ENDPOINT}\u0026#34; read -rp \u0026#34;Press ENTER to run \u0026#39;vcf context use ${VCF_CONTEXT_NAME}\u0026#39;...\u0026#34; vcf context use \u0026#34;${VCF_CONTEXT_NAME}\u0026#34; vcf context list --wide || true log \u0026#34;05 done.\u0026#34; 06-harbor-and-test.sh This step assumes:\nYou already ran step 05 so plugins exist\nDNS for harbor.vcf.local works OR you set USE_ETC_HOSTS_FALLBACK=true\nYou already have a robot account/password\nIt will:\nensure docker login works\npush busybox into Harbor project\ndeploy a test pod from Harbor using the svc-tkg context (via vcf kubectl wrapper)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } warn(){ echo \u0026#34;[WARN] $*\u0026#34; \u0026gt;\u0026amp;2; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; command -v kubectl \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;kubectl not found. Run 02-base-tools.sh first.\u0026#34; command -v docker \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;docker not found. Run 02-base-tools.sh first.\u0026#34; command -v curl \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;curl not found. Run 02-base-tools.sh first.\u0026#34; if [[ -f /etc/profile.d/vcf-ca.sh ]]; then # shellcheck disable=SC1091 source /etc/profile.d/vcf-ca.sh fi CUSTOM_BUNDLE=\u0026#34;/etc/pki/tls/certs/ca-bundle.custom.crt\u0026#34; TMP_CA=\u0026#34;${BOOTSTRAP_DIR}/harbor-ca.crt\u0026#34; get_contour_lb_ip() { kubectl --context \u0026#34;${VCF_CONTEXT_NAME}:${NS_CONTOUR}\u0026#34; -n \u0026#34;${NS_CONTOUR}\u0026#34; get svc envoy -o wide 2\u0026gt;/dev/null \\ | awk \u0026#39;NR==2{print $5}\u0026#39; } ensure_harbor_resolution() { if getent hosts \u0026#34;${HARBOR_FQDN}\u0026#34; \u0026gt;/dev/null 2\u0026gt;\u0026amp;1; then log \u0026#34;${HARBOR_FQDN} already resolves\u0026#34; return fi if [[ \u0026#34;${USE_ETC_HOSTS_FALLBACK}\u0026#34; == \u0026#34;true\u0026#34; ]]; then local ip=\u0026#34;${HARBOR_LB_IP}\u0026#34; [[ -n \u0026#34;$ip\u0026#34; ]] || ip=\u0026#34;$(get_contour_lb_ip || true)\u0026#34; [[ -n \u0026#34;$ip\u0026#34; ]] || die \u0026#34;Could not determine Harbor LB IP.\u0026#34; echo \u0026#34;${ip} ${HARBOR_FQDN}\u0026#34; \u0026gt;\u0026gt; /etc/hosts log \u0026#34;Added /etc/hosts entry: ${ip} ${HARBOR_FQDN}\u0026#34; else die \u0026#34;DNS does not resolve ${HARBOR_FQDN}. Fix DNS or set USE_ETC_HOSTS_FALLBACK=true.\u0026#34; fi } install_harbor_ca() { log \u0026#34;Downloading Harbor CA\u0026#34; curl -kfsSL \u0026#34;https://${HARBOR_FQDN}/api/v2.0/systeminfo/getcert\u0026#34; -o \u0026#34;$TMP_CA\u0026#34; [[ -s \u0026#34;$TMP_CA\u0026#34; ]] || die \u0026#34;Harbor CA download failed.\u0026#34; touch \u0026#34;$CUSTOM_BUNDLE\u0026#34; chmod 0644 \u0026#34;$CUSTOM_BUNDLE\u0026#34; log \u0026#34;Appending Harbor CA to ${CUSTOM_BUNDLE}\u0026#34; cat \u0026#34;$TMP_CA\u0026#34; \u0026gt;\u0026gt; \u0026#34;$CUSTOM_BUNDLE\u0026#34; echo \u0026gt;\u0026gt; \u0026#34;$CUSTOM_BUNDLE\u0026#34; export SSL_CERT_FILE=\u0026#34;$CUSTOM_BUNDLE\u0026#34; export CURL_CA_BUNDLE=\u0026#34;$CUSTOM_BUNDLE\u0026#34; # 401 Unauthorized is expected before login. local status status=\u0026#34;$(curl -sS -o /dev/null -w \u0026#39;%{http_code}\u0026#39; \u0026#34;https://${HARBOR_FQDN}/v2/\u0026#34;)\u0026#34; || die \u0026#34;Harbor connectivity/TLS failed.\u0026#34; if [[ \u0026#34;$status\u0026#34; != \u0026#34;200\u0026#34; \u0026amp;\u0026amp; \u0026#34;$status\u0026#34; != \u0026#34;401\u0026#34; ]]; then die \u0026#34;Unexpected Harbor /v2/ HTTP status: $status\u0026#34; fi log \u0026#34;Harbor TLS verification OK (HTTP $status from /v2/ is acceptable)\u0026#34; } docker_login_and_push_busybox() { log \u0026#34;Docker login to Harbor\u0026#34; echo \u0026#34;$HARBOR_ROBOT_PASSWORD\u0026#34; | docker login \u0026#34;$HARBOR_FQDN\u0026#34; -u \u0026#34;$HARBOR_ROBOT_USER\u0026#34; --password-stdin log \u0026#34;Push/pull busybox\u0026#34; docker pull busybox:latest docker tag busybox:latest \u0026#34;${HARBOR_FQDN}/${HARBOR_PROJECT}/busybox:latest\u0026#34; docker push \u0026#34;${HARBOR_FQDN}/${HARBOR_PROJECT}/busybox:latest\u0026#34; docker rmi \u0026#34;${HARBOR_FQDN}/${HARBOR_PROJECT}/busybox:latest\u0026#34; \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true docker pull \u0026#34;${HARBOR_FQDN}/${HARBOR_PROJECT}/busybox:latest\u0026#34; } create_pull_secret_and_test_pod() { local ctx=\u0026#34;${VCF_CONTEXT_NAME}:${NS_TKG}\u0026#34; local pod_name=\u0026#34;harbor-test-02\u0026#34; log \u0026#34;Creating imagePull secret\u0026#34; kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; delete secret harbor-tkg-pull --ignore-not-found kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; create secret docker-registry harbor-tkg-pull \\ --docker-server=\u0026#34;$HARBOR_FQDN\u0026#34; \\ --docker-username=\u0026#34;$HARBOR_ROBOT_USER\u0026#34; \\ --docker-password=\u0026#34;$HARBOR_ROBOT_PASSWORD\u0026#34; \\ --docker-email=\u0026#34;unused@local\u0026#34; log \u0026#34;Patching default ServiceAccount\u0026#34; kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; patch serviceaccount default \\ --type=merge -p \u0026#39;{\u0026#34;imagePullSecrets\u0026#34;:[{\u0026#34;name\u0026#34;:\u0026#34;harbor-tkg-pull\u0026#34;}]}\u0026#39; log \u0026#34;Running Harbor test pod: ${pod_name}\u0026#34; kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; delete pod \u0026#34;${pod_name}\u0026#34; --ignore-not-found kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; run \u0026#34;${pod_name}\u0026#34; \\ --image=\u0026#34;${HARBOR_FQDN}/${HARBOR_PROJECT}/busybox:latest\u0026#34; \\ --restart=Never --command -- sh -c \u0026#34;echo OK-from-harbor; sleep 5\u0026#34; sleep 4 kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; get pod \u0026#34;${pod_name}\u0026#34; -o wide || true kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$NS_TKG\u0026#34; logs \u0026#34;${pod_name}\u0026#34; || true } main() { ensure_harbor_resolution install_harbor_ca docker_login_and_push_busybox create_pull_secret_and_test_pod log \u0026#34;06 done.\u0026#34; } main \u0026#34;$@\u0026#34; This step focuses only on validating Harbor access, pushing a test image, and confirming that Kubernetes can pull images from the registry. Application deployment is handled separately in the next step.\nIn my testing phase i took a snapshot of the vm before running each script so that i had an easy rollback if something went wrong.\nNext is to test application deployment from Harbor.\nWhat the script does:\nThe 07-tetris.sh script performs the following actions:\nPrints the assigned IP together with the requested FQDN.\nVerifies that the target namespace exists.\nDeploys the Tetris application from Harbor.\nCreates a separate Kubernetes Service of type LoadBalancer.\nWaits for the deployment to become ready.\nWaits for the platform to assign an external LoadBalancer IP.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 #!/usr/bin/env bash set -Eeuo pipefail source /root/bootstrap/00-config.sh log(){ echo \u0026#34;[INFO] $*\u0026#34;; } warn(){ echo \u0026#34;[WARN] $*\u0026#34; \u0026gt;\u0026amp;2; } die(){ echo \u0026#34;[ERROR] $*\u0026#34; \u0026gt;\u0026amp;2; exit 1; } [[ \u0026#34;${EUID}\u0026#34; -eq 0 ]] || die \u0026#34;Run as root.\u0026#34; command -v kubectl \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;kubectl not found. Run 02-base-tools.sh first.\u0026#34; command -v getent \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || die \u0026#34;getent not found.\u0026#34; # Tetris-specific variables TETRIS_NAME=\u0026#34;${TETRIS_NAME:-tetris}\u0026#34; TETRIS_NAMESPACE=\u0026#34;${TETRIS_NAMESPACE:-$NS_TKG}\u0026#34; TETRIS_IMAGE=\u0026#34;${TETRIS_IMAGE:-${HARBOR_FQDN}/${HARBOR_PROJECT}/tetris:latest}\u0026#34; TETRIS_FQDN=\u0026#34;${TETRIS_FQDN:-tetris.vcf.local}\u0026#34; # Optional: # Leave empty to let Kubernetes/NSX assign an IP automatically. # Set this only if you want to request a specific IP from the LB pool. TETRIS_LB_IP=\u0026#34;${TETRIS_LB_IP:-}\u0026#34; ctx=\u0026#34;${VCF_CONTEXT_NAME}:${TETRIS_NAMESPACE}\u0026#34; ensure_namespace_exists() { kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$TETRIS_NAMESPACE\u0026#34; get ns \u0026#34;$TETRIS_NAMESPACE\u0026#34; \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 \\ || die \u0026#34;Namespace ${TETRIS_NAMESPACE} not found.\u0026#34; } show_input_values() { log \u0026#34;Using values:\u0026#34; echo \u0026#34; TETRIS_NAME = ${TETRIS_NAME}\u0026#34; echo \u0026#34; TETRIS_NAMESPACE = ${TETRIS_NAMESPACE}\u0026#34; echo \u0026#34; TETRIS_IMAGE = ${TETRIS_IMAGE}\u0026#34; echo \u0026#34; TETRIS_FQDN = ${TETRIS_FQDN}\u0026#34; if [[ -n \u0026#34;${TETRIS_LB_IP}\u0026#34; ]]; then echo \u0026#34; TETRIS_LB_IP = ${TETRIS_LB_IP}\u0026#34; else echo \u0026#34; TETRIS_LB_IP = \u0026#34; fi } deploy_tetris() { log \u0026#34;Deploying ${TETRIS_NAME} into namespace ${TETRIS_NAMESPACE}\u0026#34; if [[ -n \u0026#34;${TETRIS_LB_IP}\u0026#34; ]]; then cat /dev/null || true } wait_for_tetris_lb_ip() { log \u0026#34;Waiting for LoadBalancer IP for ${TETRIS_NAME}-lb\u0026#34; local lb_ip=\u0026#34;\u0026#34; local max_tries=36 local i for i in $(seq 1 \u0026#34;$max_tries\u0026#34;); do lb_ip=\u0026#34;$(get_tetris_lb_ip)\u0026#34; if [[ -n \u0026#34;${lb_ip}\u0026#34; ]]; then echo \u0026#34;${lb_ip}\u0026#34; return 0 fi sleep 5 done return 1 } show_tetris_status() { log \u0026#34;Current Tetris objects\u0026#34; kubectl --context \u0026#34;$ctx\u0026#34; -n \u0026#34;$TETRIS_NAMESPACE\u0026#34; get deploy,pod,svc | egrep \u0026#34;NAME|${TETRIS_NAME}\u0026#34; || true } print_next_steps() { local lb_ip lb_ip=\u0026#34;$(wait_for_tetris_lb_ip || true)\u0026#34; echo echo \u0026#34;========================================\u0026#34; echo \u0026#34;Tetris deployment completed\u0026#34; echo \u0026#34;========================================\u0026#34; if [[ -n \u0026#34;${lb_ip}\u0026#34; ]]; then echo \u0026#34;Assigned LoadBalancer IP : ${lb_ip}\u0026#34; echo \u0026#34;Requested FQDN : ${TETRIS_FQDN}\u0026#34; echo echo \u0026#34;Create this DNS record:\u0026#34; echo \u0026#34; ${TETRIS_FQDN} -\u0026gt; ${lb_ip}\u0026#34; echo echo \u0026#34;You can also test directly by IP first:\u0026#34; echo \u0026#34; curl http://${lb_ip}\u0026#34; echo echo \u0026#34;After DNS is created, test with:\u0026#34; echo \u0026#34; curl http://${TETRIS_FQDN}\u0026#34; else warn \u0026#34;No LoadBalancer IP assigned yet.\u0026#34; echo echo \u0026#34;Check again with:\u0026#34; echo \u0026#34; kubectl --context \\\u0026#34;$ctx\\\u0026#34; -n \\\u0026#34;$TETRIS_NAMESPACE\\\u0026#34; get svc ${TETRIS_NAME}-lb -o wide\u0026#34; echo echo \u0026#34;Once the IP appears, create this DNS record:\u0026#34; echo \u0026#34; ${TETRIS_FQDN} -\u0026gt; \u0026#34; fi } main() { ensure_namespace_exists show_input_values deploy_tetris wait_for_rollout show_tetris_status print_next_steps log \u0026#34;07 done.\u0026#34; } main \u0026#34;$@\u0026#34; At the end of the script, the output looks similar to this:\nExample output\nAssigned LoadBalancer IP : 10.33.128.x Requested FQDN : tetris.vcf.local\nCreate this DNS record: tetris.vcf.local -\u0026gt; 10.33.128.x\nIf your deployment of Tetris went fine you should be able to see this:\nEnjoy!\nNext post we create something a bit more useful like a 2 tier application.\n","date":"2026-03-08T01:12:05+01:00","image":"/p/vks-mgmt-vm-on-photon-5.0/cover.png","permalink":"/p/vks-mgmt-vm-on-photon-5.0/","title":"VKS mgmt vm on Photon 5.0"},{"content":"Ok, the VPC and Supervisor was up and running, but when trying to reach the supervisor API server, it was highly unpredictable. Trying to reach the portal i reached it 6 out of 10 times. Something was wrong and i needed a stable portal to be able to setup the endpoint with VCF-CLI.\nAfter troubleshooting ip, DNS, TLS it became clear that this was a routing issue.\nThe problem:\nI observed that access to the Supervisor API (10.31.x.x:443) was flapping:\nsometimes it worked\nother times it hung, and I could see SYN retransmissions with no response\nI started packet capture on my pfSense monitoring from my mgmt subnet interface under capture options, and under filter options a chose Ip for my supervisor api, TCP protocol and port 443, and started curl from powershell who tried to reach the api server. This was the basis for the packet capture.\nPS C:\\Users\\Vidar\u0026gt; 1..30 | % { curl.exe \u0026ndash;ssl-no-revoke -I https://10.x.x.x/api | Out-Null; Start-Sleep -Milliseconds 400 }\nWireshark showed exactly what was happening:\nthe client on mgmt subnet(10.0.x.x) sent a SYN\nbut did not receive a SYN/ACK back after several attempts\nthen suddenly it would work again on a new connection\nWhy it happened:\nOn the NSX Tier-0 (T0), there was no specific route for the management network:\nMgmt CIDR → pfSense\nBecause of that, when T0 needed to send return traffic back to my client on the mgmt subnet, it had to rely on the default route.\nThe issue was that T0 had two default routes at the same time, with equal preference:\n0.0.0.0/0 → uplink toward pfSense\n0.0.0.0/0 → 169.254.2.3 (automatically injected from the Transit Gateway / VPC)\nWhen two default routes have the same preference, NSX will often use ECMP or per-flow hashing:\nsome flows returned through 10.0.x.x(uplink toward pfSense) and worked correctly\nsome flows returned through 169.254.2.3, which was the wrong path for management traffic\nas a result, the SYN/ACK never made it back to the client\nThat is why the behavior appeared random.\nWhat fixed it:\nThe fix was to add a specific route on T0:\nMgmt CIDR → Uplink to pfSense\nYou do this the same place you configure default route on your T0, so in my case i configured a new static route from my mgmt subnet cidr with a next hop to my pfsense.\nThis forces a longest-prefix match:\ntraffic destined for mgmt subnet will always use the /24 route\nit will never use either of the default routes\nOnce that route was added, the flapping disappeared.\nWhere did the \u0026ldquo;unknown\u0026rdquo; default route come from?\nI did not manually configure 0.0.0.0/0 → 169.254.2.3.\nIt was automatically learned from the Transit Gateway (TGW) associated with the NSX VPC/Project setup.\nThat route is intended for VPC/TGW functionality, but it indirectly affected this setup because there was no specific route for the management network.\nWhat dynamic routing (BGP/OSPF) would have done:\nIf BGP or OSPF had been configured between pfSense and T0, pfSense could have advertised internal networks such as the mgmt cidr.\nIn that case, T0 would automatically have learned a specific route for the management network, and no manual static route would have been needed.\nThe key point is that OSPF or BGP are not the solution by themselves. What actually fixes the issue is giving the Tier-0 a specific route for the internal network, so return traffic does not rely on the default route.\nBefore the VPC was introduced, the Tier-0 had a single default route toward the firewall, so return traffic followed one consistent path and the setup worked. The issue only appeared after the VPC introduced a competing default route, which made the return path ambiguous and caused asymmetric routing. Adding a specific route for the internal subnet — either statically or learned dynamically through BGP/OSPF — restored a stable return path.\n","date":"2026-03-07T21:04:01+01:00","image":"/p/asymmetric-routing-after-enabling-vpc/cover.png","permalink":"/p/asymmetric-routing-after-enabling-vpc/","title":"Asymmetric routing after enabling VPC"},{"content":"After installing VCF 9.0 it was time to setup the Supervisor cluster. This would give me VKS, vSphere Kubernets Service, what we earlier called Tanzu.\nTo do this i had some pre-requisites and some options. What network to use, meaning VPC, NSX or VDS.\nBy choosing this i soon found out that i had to fix some more pre-requisites.\nI used an article from William Lam for inspiration, where he activated \u0026ldquo;NSX on DVPGs which will enable the VDS to accept traffic from NSX Edge and thus allowing us to select shared VLAN for both NSX and ESX Host Tunnel Endpoint Protocol (TEP)\u0026rdquo;. I will not do that. My main purpose with the setup was to gain more experience with VPC and how to use it with K8s.\nLink to his article here.\nYou might say that a VPC is the equivalent of a T1 GW, and a Tier-1 Gateway is infrastructure-centric, while VPC is tenant-centric and designed for simpler, more isolated, cloud-like networking.\nBased on the requirements in the installation and some testing i needed a separate external connection:\nIn the default project you have a default external connection, and that is used for your OTHER vpc`s , and the reason for this is that they have different ip blocks, and i did not want to mix them.\nNext was to create a new project in NSX.\nHere you connect the external connection you created in the previous step, the external IP block, connection to edge cluster and T0, but also the option to separate it with folders in vcenter and add to DFW.\nThen it was creation of a separate connectivity profile.\nNext was editing the VPC Service Profile where i added DNS and NTP servers.\nWith this step ready i could then make my VKS workload VPC.\nHere i added my new connectivity profile, used the default service profile . Private VPC IP CIDR is the internal network in the VPC, so i chose an ip block with /20 mask. If you then need to access a service in one of those subnets, you can assign an ip from the external ip block, so that you can reach it outside the VPC.\nSo now i have defined:\nExternal connection under networking in default project\nNew NSX Project \u0026ldquo;Supervisor\u0026rdquo;\nNew connectivity profile\nNew Private Transit gateway ip block\nNew External ip block\nEdited the service profile with dns and ntp.\nCreated a VPC\nNew Private VPC IP block\nNow we have covered the most of the network for the supervisor, but we need mgmt and we need VIP for the loadbalancer.\nThe mgmt needs to be routable and connected to your esxi/vcenter, so instead of making a new vlan, defining it in the trunk on the physical switches and creating an interface in the firewall , setting up rules, i did the quickfix, and that was setting up a small mgmt subnett with a /28 mask, and defining it in the firewall as a virtual IP on the same interface as the rest of the mgmt. This gave me instant routing to these ip`s for the supervisors.\nIf you have more patience, please do the vlan/trunk setup, but i wanted to proove my theory for the network setup, and test K8s.\nNext up was the ip for the loadbalancer. This is automatically picked from the external ip block, so i had that covered.\nSo now i also had:\nMgmt subnet for the supervisors\nIp for the loadbalancer\nAs i wrote earlier, the reason for separating this is that IF you create this in the default project, where you also have other ip blocks for other VPC`s the profile will randomly pick from that for default outbound nat address.\nSo if i had i pod config where each pod had its own vpc, and picked from external ip blocks pr. pod, you could suddenly end up with a nat address in your supervisor vpc from one of those pod`s.\nThis way you isolate the traffic and separate ip blocks.\nNow you can start the \u0026ldquo;activate Supervisor\u0026rdquo; by right clicking your cluster in the inventory in vcenter.\nthis will take you to this:\nChoose advanced topologies:\nIt will take you to the choices for what network option to choose for the Supervisor.\nWe choose VCF networking with VPC.\nNext is the Supervisor name, cluster or zone, i chose cluster and set a zone name.\nThe reason for this is, i only have one cluster i this scenario. If i had several clusters i could create a zone containing several clusters with compute and resources that kubernetes could use.\nOne thing to notice here is the option for control plane HA.\nIf you don`t check this , you will have 1 control plane vm, and you later need to activate HA to get 2 more.\nCheck this, and you get 3 right away.\nFor storage i chose the default vsan storage policy wich is R1. If you in your environment need more redundancy, pick a more redundant policy like R5, R6 or mirror if you have a stretch cluster.\nRule of thumb:\nR1 - performance\nR5/R6 - capacity and redundancy\nNext up is the mgmt for the supervisors.\nI use:\nstatic\nthe portgroup for mgmt that connects to the virtual ip\nA range of at least 5 adresses ,xx.0.0.0/28\nmask 255.255.255.240\ngateway xx.0.0.1\ndns\ndomain vcf.local\nntp\nNow we have the workload network:\nHere you see the NSX project we created, and also the VPC connectivity profile that gives us the external IP block and the private transit gateway ip block.\nYou add the Private VPC CIDR that you configured in your VPC earlier.\nLeave the Service CIDR, its for internal Kubernetes ClusterIP Services, as long as it does not interfere with IPs of Supervisor Management components (VC, NSX, ESXs, Management DNS, NTP) and should not overlap with other datacenter IPs communicating with workloads.\nAdd DNS and NTP.\nLast config is the size of the Supervisor vms and the fqdn for the API server.\nSmall is default.\nAPI - Server:\nComma-separated DNS names used to access the API Server. The names will be used as the \u0026lsquo;SubjectAltName.DNS\u0026rsquo; field of the API Server certificate. The Load Balancer IP address is automatically added to the IP field and should not be included here.\nThen you are almost there. I checked the box for export configuration.\nThis way you can restore your config if you mess it up in some way.\nand thats it, last page summarizes it all, and you can press finish.\nThe whole thing takes about an hour, maybe less, and you may need to configure Harbour, setup a content library for the supervisor images and other types of post-provision tasks related to kubernetes.\nOne thing i did was to wait for the api server to become available, and i added the ip in my dns so that it would resolve easily for FQDN.\nHEADS UP:\nMy setup on VCF 9.0 also stopped at configuring nodes, meaning the esxi nodes. The problem is the version of a VIB that is not being copied from vCenter to the hosts. There is an article on broadcom covering some of this, and i can show you the steps i did.\nThis was Broadcom`s solution. From my perspective I found it was lacking some info:\nOn vCenter cli:\nrun:\n1 2 grep -E \u0026#34;unable to find desired version|Unable to find image info of desired version\u0026#34; \\ /var/log/vmware/wcp/wcpsvc.log | tail -n 20 You should get a specific spherelet / Kubernetes desired version (this is what Supervisor expects), for example:\nv1.30.5+vmware.4-fips-vsc9.0.0.0-24686447\nMatch that version to the Spherelet VIB files that exist on vCenter\nSpherelet VIBs are stored under the patch-store folder (as the KB indicates). On vCenter, list the available VIBs:\n1 2 cd /storage/updatemgr/patch-store/hostupdate/vmw/vib20/spherelet ls -1 The files are typically named like:\nVMware_bootbank_spherelet_.vib Pick the VIB whose matches the minor/patch version from step 1.\nExample match logic\nIf wcpsvc.log shows desired Kubernetes version like:\nv1.30.5+\u0026hellip;\n…and you have VIBs for 30.5, 29.3, and 28.2, then the correct match would be:\nVMware_bootbank_spherelet_9.0.1.30.5.0-24526496.vib\nNote about build numbers:\nIt’s normal that the build number in the “desired image info” (e.g. 24686447) does not equal the build number in the VIB filename (e.g. 24526496).\nThe important part is that the minor/patch matches (e.g. 30.5).\nNow that you now the correct version:\nCopy the correct VIB from vCenter to an ESXi host\nOn vCenter:\n1 2 cd /storage/updatemgr/patch-store/hostupdate/vmw/vib20/spherelet scp VMware_bootbank_spherelet_9.0.1.30.5.0-24526496.vib root@:/tmp/ Install the VIB on esxi:\non esxi:\n1 esxcli software vib install -v /tmp/VMware_bootbank_spherelet_9.0.1.30.5.0-24526496.vib Then start the activate Supervisor again, and nodes should configure fine.\nThen you can test your api server:\nin a browser add your api server IP:\nIt should give you this page for downloading the vcf-cli.\nFor my next post i will create a photon mgmt vm for all vcf-cli , kubectl and docker specific commands.\nEnjoy!\n","date":"2026-02-06T01:03:33+01:00","image":"/p/supervisor-cluster-with-vpc-on-vcf-9.0/supervisor-cluster-with-vpc.png","permalink":"/p/supervisor-cluster-with-vpc-on-vcf-9.0/","title":"Supervisor cluster with VPC on VCF 9.0"},{"content":"After trying a deploy like described in the previous post this was my config:\n1 New-HoloDeckInstance -Version \u0026#34;9.0\u0026#34; -InstanceID \u0026#34;VB\u0026#34; -CIDR \u0026#34;10.1.0.0/20 -vSANMode \u0026#34;ESA\u0026#34; -WorkloadDomainType \u0026#34;SharedSSO\u0026#34; -NsxEdgeClusterMgmtDomain -DeployVcfAutomation -DeploySupervisor -LogLevel \u0026#34;INFO\u0026#34; -Site \u0026#34;a\u0026#34; -DepotType \u0026#34;Offline\u0026#34; I found out that a full deploy takes up too much resources on my physical host, because the automation vm consumes 1 nested host alone with 24 cores.\nThat is twice the amount of cores compared to the other 3 hosts, because the automation vm needs/demands it.\nA full deploy is described here in this picture under VMware Cloud Foundation:\nIn my case the physical DELL host had 2 sockets,56 cores with a total of 67Ghz and the nested esxi with 1 automation vm used 31Ghz of that, BEFORE doing any setup or labstuff.\nFull deploy also creates a workload cluster with its own NSX and vCenter as well as a Supervisor cluster, so my host where running at 100%+ cpu before i even logged in and tried anything.\nI deleted the environment and started over. This time i only created a mgmt cluster and also had to remove the \u0026ldquo;deploySupervisor\u0026rdquo; setting. This is because it will not let you install it in a mgmt cluster, only when workload is defined.\nBut deploying it afterwards in the mgmt cluster from vCenter should be possible. (from a LAB perspective, not recommended in production.)\nSo i ran the config again:\n1 2 3 New-HoloDeckConfig -Description -TargetHost -Username -Password example: New-HoloDeckConfig -Description \u0026#34;VCF\u0026#34; -TargetHost x.x.x.x -Username root -Password VMwarexxx! This will give you a config ID:\n1 2 3 4 5 6 PS /\u0026gt; get-HoloDeckConfig ConfigID : vqg1 Description : VCF ConfigPath : /holodeck-runtime/config/vqg1.json Instance : vbi Created : 12/28/2025 8:22:39 PM New-HolodeckInstance command options:\nNew-HoloDeckInstance -Version [-InstanceID ] [-CIDR \u0026lt;String[]\u0026gt;] [-vSANMode ] [-WorkloadDomainType ] [-NsxEdgeClusterMgmtDomain] [-NsxEdgeClusterWkldDomain] [-DeployVcfAutomation] [-DeploySupervisor] [-LogLevel ] [-ProvisionOnly] [-Site ] [-DepotType ] [-DeveloperMode] []\nMy new version was:\n1 New-HoloDeckInstance -Version \u0026#34;9.0\u0026#34; -InstanceID \u0026#34;VBI\u0026#34; -CIDR \u0026#34;10.1.0.0/20\u0026#34; -vSANMode \u0026#34;ESA\u0026#34; -ManagementOnly -NsxEdgeClusterMgmtDomain -DeployVcfAutomation -LogLevel \u0026#34;INFO\u0026#34; -Site \u0026#34;a\u0026#34; This only installs VCF Mgmt cluster (4 nodes) with a NSX Edgecluster and also deploys VCF Automation. You can then deploy a workload cluster after you have the Mgmt cluster up and running.\nAfter the script is started it needs a few inputs:\nthe disk for HoloDeck\nThe Network\nThe token from broadcom support\nThe info for the offline depot VM\nThe script starts prepping the hosts, then the VCF installer.\nAfter this it checks the Depot and downloads files if needed.\nWhen this is ready you can open the desktop in browser: holorouterip:30000 and add 10.1.10.250 in firefox inside the vnc where you can follow the rest of the install process.\nIn my case it deployed successfully (again) ,and i had a fully working VCF9 lab to play around with.\nBut\u0026hellip;\u0026hellip;\nWebtop/VNC is not very fast, and really annoying that you get latency when trying to click around in Operations or SDDC manager.\nSo, i looked around for a better alternative.\nHow could i access the Holodeck environment with all of its vlans without exposing them to the outside world, and still get good performance in the UI?\nI think i found it, and it was SSH Socks proxy.\nThe way it works is that you connect from your mgmt vm via SSH port forwarding to the holorouter and into the Holodeck environment.\nFirst step, i had to open (admin)powershell and run:\n1 ssh -N -D 1080 user@ What does the flags mean?\n-D 1080 -\u0026gt; starts SOCKS5-proxy on local port 1080\n-N -\u0026gt; no shell ( tunnel only)\nuser@host -\u0026gt; Holorouter\n1 2 3 4 5 6 7 PS C:\\Windows\\system32\u0026gt; ssh -N -D 1080 root@192.168.xx.xxx The authenticity of host \u0026#39;192.168.xx.xxx (192.168.xx.xxx)\u0026#39; can\u0026#39;t be established. ED25519 key fingerprint is SHA256:aMcRyebFuXgyF/Qv0q4SN4Uxfemtwao0tWxYTTTxxxx. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added \u0026#39;192.168.xx.xxx\u0026#39; (ED25519) to the list of known hosts. (root@192.168.xx.xxx) Password: Leave the terminal open, it is now the “entrance gate” to the isolated environment.\nIn my case it failed for several reasons, and this is how i fixed it.\nI have 2 ip`s configured in my mgmt vm so i also changed the metric on the interfaces to be sure the vm prioritized the traffic between the mgmt vm and the holorouter.\nHere it failed because i had not changed ssh config yet.\n1 2 3 4 5 PS C:\\Windows\\system32\u0026gt; ssh -N -D 1080 root@192.168.xx.xxx (root@192.168.xx.xxx) Password: channel 2: open failed: connect failed: Name or service not known channel 2: open failed: connect failed: Name or service not known channel 3: open failed: connect failed: Name or service not known If it fails, you need to verify ssh.\n1 2 3 4 5 6 7 8 PS C:\\Windows\\system32\u0026gt; Test-NetConnection 192.168.xx.xxx -port 22 ComputerName : 192.168.xx.xxx RemoteAddress : 192.168.xx.xxx RemotePort : 22 InterfaceAlias : HoloMGMT-12xx SourceAddress : 192.168.xx.xx TcpTestSucceeded : false The holorouter is a Photon VM, not configured for this ssh config, so you need to edit your sshd_config. ( maybe iptables as well if ssh is stopped after a reboot)\nIPtables: run this in your holorouter as root.\n1 2 iptables -P INPUT ACCEPT iptables -F INPUT what it does?\niptables -P INPUT ACCEPT Sets default policy INPUT to ACCEPT\niptables -F INPUT removes all existing INPUT-rules\nResult:\nProve that no input gets blocked by iptables in the photon vm.\nNow using VI to edit SSHD_CONFIG on the Holorouter:\n1 vi /etc/ssh/sshd_config Here you need to add/change these values:\n1 2 3 4 5 6 AllowTcpForwarding yes AllowAgentForwarding yes PermitTunnel yes Match User root AllowTcpForwarding yes if you want to do it the right way and more secure, also add:\n1 2 Match Address 192.168.xx.xx AllowTcpForwarding yes This allows forwarding to your mgmt vm only, and makes it more secure.\nesc, wq! to end the VI session, and you are done with the sshd_config.\nThen do a restart of sshd\n1 2 3 systemctl restart sshd systemctl status sshd This change would also be a good idea if you have more than one holodeck host, just create a small mgmt vm for each holodeck, and set it up with socks proxy and its own mgmt vm address, specific for that Holo-environment.\nIf you now try again with:\n1 ssh -N -D 1080 user@ no errors\nno output\ncommand \u0026ldquo;hangs\u0026rdquo; -\u0026gt; its a success\nYou can verify the port with:\n1 2 3 PS C:\\Users\\bervid\u0026gt; netstat -ano | findstr 1080 TCP 127.0.0.1:1080 0.0.0.0:0 LISTENING 24096 TCP [::1]:1080 [::]:0 LISTENING 24096 Why this happens:\nPhoton OS / appliance-images has often:\nAllowTcpForwarding no as default\nMatch blocking specially for root\nSecurity measures\nWhen this works you have:\nPing\nTCP 22\nSSH login\nSocks Proxy\nDNS via SOCKS\nAnd you dont need webtop👍\nNext step is installing Firefox in your mgmt vm and configuring the proxy.\nGo to firefox settings and network:\nSet the socks host to 127.0.0.1 and the desired port, in this case 1080.\nCheck also the Proxy DNS socks v5\nYou should then be able to start a firefox tab and add one of these adresses:\n1 2 3 https://vc-mgmt-a.site-a.vcf.lab https://ops-a.site-a.vcf.lab https://auto-a.site-a.vcf.lab This should resolve FQDN`s perfectly from your mgmt vm outside the nested Holodeck environment. (with your socks proxy firefox only).\nStopping the powershell terminal running the tunnel stops the connection.\nThere are probably more than one way to fix this, ssh socks proxy worked for me, webtop not so much.\nSo now i fixed the remote control performance, but still was using about 85-95% cpu in my host.\nOne way i might fix that can be to extend the Holo-network / portgroups to a secondary host with direct connect cables, in this case , 10GB DAC cables.\nThis way i can move my automation vm to a secondary host and use primary host for mgmt and Supervisor/Tanzu.\nMore about that in my next post.\n","date":"2026-01-05T20:35:40+01:00","image":"/p/holodeck-vcf9-part-2/cover.png","permalink":"/p/holodeck-vcf9-part-2/","title":"Holodeck \u0026 VCF9 part 2"},{"content":"Finally there are some news about the transition from the core.vmware.com pages ,and Holodeck info is now here:\nhttps://vmware.github.io/Holodeck/\nHere you can find info about configuring either VCF5.2 or VCF9.\nThe components and prereq has changed a bit, and now you have a new Holorouter with a lot more functionality. The Holodeck guide describes this well and you basically deploy an OVA , same network prereq , but also an extra setting called webtop.\nWebtop actually gives you a console in a browser on port:30000. Here you can do the tasks that you earlier did in the Holoconsole vm.\nWhen deployed you need to copy 2 files into the holorouter, more specifically to the directory: holodeck-runtime/bin/9.0/\nThe 2 files are the SDDC manager ova and the esxi 9.0 iso.\nNow you ssh to holorouter and start the config prep. first start powershell with the pwsh command.\nNext is the config:\nNew-HoloDeckConfig -Description -TargetHost \u0026lt;Target vCenter/ESX IP/FQDN\u0026gt; -Username -Password example:\nNew-HoloDeckConfig -Description \u0026ldquo;My VCF LAB\u0026rdquo; -TargetHost x.x.x.x -Username root -Password VMware123!\nThis will give you a config ID, and you are now ready for deploy, …or are you? Well, my experience was that i needed my download token from broadcom support page, as well as the offline depot vm. So i saved my token and deployed my offline depot vm.\nNow i was ready, and lets just say it was a few steps back and forth before i got to this stage\u0026hellip;\u0026hellip; One step back was that the config would not accept my token or the depot if i chose the online depot. So to make a long process a bit more predictable, this was needed, in my opinion.\ni then ran the New-HolodeckInstance command:\nNew-HoloDeckInstance -Version [-InstanceID ] [-CIDR \u0026lt;String[]\u0026gt;] [-vSANMode ] [-WorkloadDomainType ] [-NsxEdgeClusterMgmtDomain] [-NsxEdgeClusterWkldDomain] [-DeployVcfAutomation] [-DeploySupervisor] [-LogLevel ] [-ProvisionOnly] [-Site ] [-DepotType ] [-DeveloperMode] []\nMy version was:\nNew-HoloDeckInstance -Version \u0026ldquo;9.0\u0026rdquo; -InstanceID \u0026ldquo;VB\u0026rdquo; -CIDR \u0026ldquo;10.1.0.0/20 -vSANMode \u0026ldquo;ESA\u0026rdquo; -WorkloadDomainType \u0026ldquo;SharedSSO\u0026rdquo; -NsxEdgeClusterMgmtDomain -DeployVcfAutomation -DeploySupervisor -LogLevel \u0026ldquo;INFO\u0026rdquo; -Site \u0026ldquo;a\u0026rdquo; -DepotType \u0026ldquo;Offline\u0026rdquo;\nThere is a good explanation for all the values on the holodeck page, find the combination that fits your needs.\nAfter script is started it needs a few inputs:\nthe disk for HoloDeck\nThe Network\nThe token from broadcom support\nThe info for the offline depot VM\nThe script starts prepping the hosts, then the VCF installer.\nAfter this it checks the Depot and downloads files if needed.\nWhen this is ready you can open the desktop in browser: holorouterip:30000 and add 10.1.10.250 in firefox where you can follow the rest of the install process.\nHopefully it deploys successfully at this stage,and you have fully working VCF9 lab to play around with.\n","date":"2025-09-22T01:19:22+01:00","image":"/p/holodeck-vcf9/cover.png","permalink":"/p/holodeck-vcf9/","title":"Holodeck \u0026 VCF9"},{"content":"After the migration of the VMware website to Broadcom there where some confusion with the links we where used to, and William Lam has made a great list:\nhttps://williamlam.com/2024/05/useful-links-for-vmware-by-broadcom-day-2-transition.html\nI listed some of them here:\nCustomer Connect/Support\nhttps://support.broadcom.com/\nSiteID Request Tool: https://support.broadcom.com/web/ecx/my-site-id\nDownloads:\nhttps://support.broadcom.com/group/ecx/downloads\nPatch Downloads:\nhttps://support.broadcom.com/group/ecx/downloads\nKnowledge Base:\nhttps://support.broadcom.com/web/ecx\nVMware Compatibility Guide (VCG/HCL)\nhttps://compatibilityguide.broadcom.com/\nVCF Holodeck Toolkit:\nhttps://forms.gle/sY4tYse2ARxEK1L4A\n","date":"2025-03-19T23:40:56+01:00","image":"/p/useful-vmware-links-2025/useful-vmwarelinks.png","permalink":"/p/useful-vmware-links-2025/","title":"Useful VMware links 2025"},{"content":"\nLooking at the supportfiles for testing in your VCF-lab, we need some support vms, and the source iso is not included in the files.\nYou can download the Ubuntu here:\n$src = \u0026ldquo;https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.ova\" $ova = \u0026ldquo;ubuntu-18.04-server-cloudimg-amd64.ova\u0026rdquo;\nrename to:bionic-server-cloudimg-amd64.ova\nthen put it here in your holo-console vm:\nc:\\users\\administrator\\downloads\\bionic-server-cloudimg-amd64.ova\nNow you are ready to open the Auto-Lab and run the scripts.\nTo my surprise there where some errors in one of the scripts, the VMBuild.ps\nlook for:\n$scripts += Get-ChildItem -Path $scriptsDirectory -Filter \u0026ldquo;0*.ps1\u0026rdquo; | Select-Object -ExpandProperty FullName\nremove the zero, or the script only show you files starting 0 end end with .ps\nthen you get a menu for installing the support files/vm.\nyou start at nr.1 and work your way down, and doing this you will get a Holo-Template vm in your vcenter directory.\nI also found that the json file: template-generate-v1.json had a typo in the section where it defines and cidr. you need to correct this to cidr 230 , the same as the IP.( it says 236 in the cidr)\n","date":"2025-03-19T23:14:45+01:00","permalink":"/p/holo-lab-support-files-5.1/","title":"Holo-Lab Support files 5.1"},{"content":"So easy installer is complete, now what?\nWell, you need a few more steps if you want orchestrator fully integrated with vCenter and vRa.\nCheck your inventory, and verify vAPI and vSphere vCenter Server.\nwe start with vAPI. Go to workflows, and search for metamodel.\nwe will run import vAPI metamodel, because this adds endpoint at the same time.\nThis gives the green \u0026ldquo;completed\u0026rdquo;, so we are OK.\nNext up is the vCenter, so we search in workflows for add vCenter instance\nand we run it.\nHere we add the fqdn for vcenter , and to avoid java runtime issues, do not choose per user session, add the login for vCenter and run it.\nok, so now we are ready to use workflows from service broker, so lets try that.\nGo to service broker, choose content sources, and add new, add orchestrator source.\nHere i set a name for my content source and choose a workflow.\nI added the workflow \u0026quot; add disk\u0026quot;, so \u0026ldquo;create \u0026amp; import\u0026rdquo;\nThen we need to add it to a content sharing policy.\ni opened my policy-2, and added the vlc-workflows\nThis means it will be shared along with my other content in my project.\nLooking at my content, \u0026ldquo;add disk\u0026rdquo; is now available.\nIt is now available in my catalog, and we are ready to try:\nwe choose a deployment name, and search for the VM.\nsearch for your datastore, disk mode, index, size, bus nr. and set disk to thin provisioned.\nWatch your deployment, it completes, and you are ready to log in to your vm and partition your disk.\nThis was just an example, and orchestrator has around 600 preconfigured workflows, and you can also create your own if you want, good luck orchestrating !\n","date":"2025-02-23T00:39:46+01:00","permalink":"/p/aria-vcenter-and-orchestrator-integration/","title":"Aria: vCenter and Orchestrator integration"},{"content":"\nWhen deploying a VCF installation with VLC Holodeck you need to deploy the Aria software, that means Aria Lifecycle manager, Aria Identity manager and Aria Automation. We have a few others as well, but will be covered in another post.\nJust one tip:\nCheck if your console-vm that you run the installer from can reach the Aria networks, AVN. They are called Region-A with subnet 10.50.0.0/24 and X-Region with subnet 10.60.0.0/24.\nMy experience was that i was missing some routing on my console vm, so the installer could not reach the LCM during the install, and failed.\nSo, try ping to 10.60.0.1 which is the X-Region gateway. If this works, no problem, but if not:\ndo a \u0026ldquo;route print\u0026rdquo; in CMD on your console vm to check your routes.\nThen in cmd, write:\n\u0026ldquo;route add 10.50.0.0 mask 255.255.255.0 10.0.0.221\u0026rdquo;\nand\n\u0026ldquo;route add 10.60.0.0 mask 255.255.255.0 10.0.0.221\u0026rdquo;\nthis will give you a OK if it works.\nThis creates routing info between the AVN`s and your console and reports it to your GW 10.0.0.221.\nThere are several ways to deploy Aria, but i think the easiest would be to use the Aria easy installer( at least in a Holodeck), an ISO file with all three products included.\nIn the latest version this is called:\nVMware-Aria-Automation-Lifecycle-Installer-23508932.iso\nIt`s a 22GB iso file and you run it by mounting it in your OS, (doubleclick in windows), browsing to:\nchoose the win32 folder.\nChoose the installer application file.\nGoing past Introduction and EULA, you are defining the vCenter that the Aria product will communicate with and use as a resource.\nNext was selecting a location for the vms in vcenter, so i created a vm folder in vCenter called Aria Suite, and chose this in the wizard.\nNext is choosing the compute resource , so i chose my mgmt cluster, and for storage my vsan.\nHere we choose the network segment/portgroup for Aria, and in my case i let VCF/ Holodeck create two networks (AVN) for this.\nAdd name and IP for your LCM\nAdd name and IP for your IDM\nHere you define your default user configadmin in IDM and node size. I will not choose FIPS, there is enough troubleshooting within Holodeck.\nAutomatic sync of users\nAdd name and IP for your Aria Automation vm. This will also give you the default range for the K8s pod within Aria.\nEnvironment name and license, FIPS and node size.\nSummary, verification and submit.\nThis will go on for a while, approximately an hour or so.\nThis will eventually give you installation OK, and you are ready to start your Aria experience.\n","date":"2025-02-03T00:00:51+01:00","image":"/p/aria-easy-installer-in-holodeck/aria-easyinstaller-in-holodeck.png","permalink":"/p/aria-easy-installer-in-holodeck/","title":"Aria easy installer in HoloDeck"},{"content":"\nSo i have been using VMware Fusion a while now on my Intel based MacBook Pro, but my latest MacBook Pro had ARM cpu, and that seemed to change a few things. The most obvious was the OS support, but while using e.g. Win11 as a vm in fusion i found that when leaving vm unattended and not powering it down correctly after every use, made it ask if i had copied or moved it when trying to power on.\nSaying i copied it did not work, because the next message was that it was not able to open a disk or snapshot it depended on.\nThis is something i did not have a problem with on the Intel based Macs.\nThe issue is that it creates a lock file, and therefore stops you from opening and reading the disk it depends on.\nThe fix(that worked for me) is actually quite easy.\nRightclick your vm and choose \u0026ldquo;show in finder\u0026rdquo;\nRightclick your vm again and choose \u0026ldquo;show package contents\u0026rdquo;\nhere you have all the vm files and some of these are named \u0026ldquo;vmname.vmx.lck\u0026rdquo;\njust remove/delete the *.lck files.\nBoot up and it should give you no error.\nThe reason for the lck files is to have a lock on the vmdk file when vm is running, and my theory is that an unclean shutdown/suspend could corrupt this and give you problems like this.\nWhy this happens more frequently on ARM vs Intel is something we need to ask VMware / Broadcom.\n","date":"2025-01-27T11:10:32+01:00","permalink":"/p/fusion-13.x-cannot-open-the-disk-or-one-of-the-snapshot-disks-it-depends-on.-module-disk-power-on-failed./","title":"Fusion 13.x 'Cannot open the disk or one of the snapshot disks it depends on. Module 'Disk' power on failed.'"},{"content":"\nOk, so we are installing the Holodeck Toolkit 5.1\nThere is still missing some documentation from the Broadcom site, so lets hope they migrate everything to the new broadcom pages soon and makes them available. In the mean time\u0026hellip;\nUse the google link to download the holodeck sw:\nhttps://docs.google.com/forms/d/e/1FAIpQLSelfYYIIRkfvgniLBJxXbHv62hmyxD_GAtzlh-lzi2YFiUICg/viewform\nHere you will come to a form with download links after you have submitted a form:\nHolodeck Toolkit Supporting VCF 4.5.x, 5.0 \u0026amp; 5.1.1 Welcome to utilizing the Holodeck toolkit you can download the zip files by going to\nhttps://broadcom.box.com/v/holodeck-2-0\nfor Holodeck 2.0 (supporting VCF 4.5 and 5.0)\nhttps://broadcom.box.com/v/holodeck-511\nfor Holodeck 5.1.1(supporting VCF 5.1.1 with License Later)\nSo to the documentation. The overview pdf is a heads up about what you need, software/hardware as well as some prerequisites. This is for the 2.0 Toolkit, and not for the 5.1, and yes, there are some newer versions, and i have a copy of the files further down in this post. As i said, waiting for updated documentation :)\nholo_toolkit_20_overviewDownload\nThe files needed for 5.1 in my downloads folder:\nThe host-prep file gives you the info you need to setup your physical host with the correct vSwitch and portgroups.\nholo-setup-host-prepDownload\nThe VCF-Lab guide shows you how to use the Lab constructor and some explanations for some of the scripts and files used.\nVCF Lab Constructor Install Guide 5.1Download\nHardware:\nI am deploying on a Dell R730XD with:\nESXi 8.0 update3\nCPU: 2 sockets Intel Xeon E5-2680 v4 , 28 cores in each cpu.\nMemory: 768GB\nDisk: 4TB SSD and 8TB SAS\nNetwork 4x 10gb sfp+\nDeploy:\nAfter following instructions for the host setup and downloading all the software mentioned in the overview pdf i was ready for the createiso.ps to deploy my console vm.\nIf you see that there are older versions in the overview you can download the new ones and update the powershell script in the path as shown below.\nWhen editing the CreateISO.ps, my experience was to not add license in this step, so leave the 4 license choices unchanged and add the license after bring up.\nHow to setup the custom ISO:\nholo-setup-custom-isoDownload\nOn my esxihost i had already setup a HOLOMGMTvm just to deploy from using a win2019 from the download list in the overview pdf.\nI did some small adjustments in the path to some of the software, and finally the script ran as supposed to.\nNext was copying the iso to my esxi host and attach it to the Holo-Console vm that you create in the pdf holo-setup-deploy-console.\nholo-setup-deploy-consoleDownload\nSetting up the Holo-Console is described in this guide and it suggests resources here:\nI would suggest to increase the memory and disk to 16GB and 300GB disk. This is your mgmt vm inside the Holodeck, so as long as you are not exhausted on resources, this is a good idea.\nAfter booting the vm on the iso file, the rest of the process is automated, so sit back and wait, this takes a little while with several reboots.\nNext up is the holo router, based on VMware Photon OS.\nholo_toolkit_20_deploy_routerDownload\nThis is a *.ova file that you just deploy to your host.\nBecause the Holo-Console sits on vlan10 with a 10.0.0.x address, wich is the internal Holo vlan, you need this router to forward port3389 (RDP)from your mgmt network(maybe 192.168.12.x) and into vlan10.\nFollow the *.ova install and under additional settings add your external address in your mgmt network.\nIf you did the iso process correctly and configured your router correctly you should now be able to RDP to your Holo-Console and start the bring up process from the VCF-Lab constructor.\nHere`s your Holo-Console ready to go.\nYou find the files needed in folders Cloudbuilder and VLC.\nBrowsing into the VLC folder you go to VLC-Holo-Site-1.\nHere`s a guide how to run the VLC-Gui:\nholo-setup-run-vlc-guiDownload\nHere we run the VLCGui.ps in powershell.\nThis gives us the VCF LAB constructor.\nFill in the host-info and choose automatic.\nFill the rest , and choose validate. This will give you the \u0026ldquo;construct\u0026rdquo; option if all info is correct.\nNow the bring up process is running and this will take about 3-4 hours. After about 30 minutes you can point your browser to the cloudbuilder vm on 10.0.0.221, where you can track the bringup process like you do in a normal VCF install.\nFailures in this process comes up here in the GUI, with the possibility for fixing based on the error message and choosing retry instead of deploying from scratch. There are also logfiles in the /var directory of the cloudbuilder vm.\nGo do something fun, and come back to a ready to go VCF LAB.\nYou are now ready to login to your VCF Lab and SDDC manager.\nBut for login and users, take a look at C:\\VLC\\VLC-Holo-Site-1 folder on your Holo-Console vm.\nHere you have a *.html file with all the ip`s and passwords you need in your Holodeck.\nThen it`s just log in and start your lab.\n","date":"2025-01-24T01:37:38+01:00","image":"/p/holodeck-toolkit-5.1-install/holodeck-toolkit-5.1-install.png","permalink":"/p/holodeck-toolkit-5.1-install/","title":"Holodeck Toolkit 5.1 install"},{"content":"\nSome times you may need to copy a script or textfile from your mgmt desktop to a vmware mgmt vm. In my case, this happened during VCF install and configuring the CloudBuilder VM. This is a linux vm and is used for the preparation of VCF for importing the excel file with all the info before deployng VCF.\nI needed the json file for the VSAN HCL defined with patch in the CloudBuilder VM, so either use a tool like winscp or you can do like this:\nlog in with ssh/putty to where you need the file.\nuse a text editor like VI\nin my case i needed a file called \u0026ldquo;all.json\u0026rdquo;\nOpen the file on your desktop with a tool like Notepad++ copy all the text/code from file on your desktop.\nIn your SSH putty session to a server, create a new file with VI, \u0026ldquo;vi all.json\u0026rdquo;\nwrite the letter I for insert\npaste the info directly into your SSH session , adding it to your new file.\nyou can do paste/insert with a right-click in the putty session\nThe example text is copied, save with esc, write letters :wq and enter. If you are not familiar with VI, this means w for write and q for quit\nIf you need to change permissions on your new file use chmod 755 all.json\nI did a ls -lh to see permissions on the file. After chmod i now have read/write/execute on the file.\n","date":"2025-01-08T20:34:25+01:00","permalink":"/p/copy/paste-with-vi-to-esxi/","title":"Copy/paste with VI to esxi"},{"content":"\nAs Broadcom aquired VMware, the Holodeck web pages have been moved and migrated, so for a while now, Holodeck setup info and documentation has been unavailable. They where located on core.vmware.com, and are (according to Broadcom) in a transition phase.\nThere is some info here:\nhttps://www.vmware.com/resources/resource-center?category[]=Hands-on%20Labs\u0026amp;term[]=Holo\nThe setup files for holodeck 4.5.2 /5.0 / 5.1 can be downloaded using this form:\nhttps://docs.google.com/forms/d/e/1FAIpQLSelfYYIIRkfvgniLBJxXbHv62hmyxD_GAtzlh-lzi2YFiUICg/viewform?pli=1\nAs i mentioned some of the documentation is still missing from the broadcom pages, and this was all the lab exercises and how to configure/test your Holo.\nBut for a VCF deployment to test SDDC functionality or supervisor cluster for Tanzu everything is included. Using the Aria easy installer after Holo is installed you can get a lifecyclemanager, Identity manager and Aria automation.\n","date":"2025-01-08T19:41:18+01:00","image":"/p/holodeck-2024/25/holodeck-2024-2025.png","permalink":"/p/holodeck-2024/25/","title":"Holodeck 2024/25"},{"content":"\nStarting from v.8.0u2, esxi changed how you can upgrade your esxi host from within CLI, esxcli software vib install -d \u0026ldquo;path to depot file\u0026rdquo; do not work anymore.\nKB here: https://knowledge.broadcom.com/external/article/380215/esxi-version-change-is-not-allowed-using.html\nYou now have to define depot location and esxi profile instead.\nEXAMPLE: esxcli software profile update \u0026ndash;depot=\u0026quot;/path to depot file\u0026quot; \u0026ndash;profile=\u0026ldquo;name of esxi profile\u0026rdquo;\nTo find esxi profile for your specific update you can run this command:\n\u0026ldquo;esxcli software sources profile list -d /vmfs/volumes/esx04_local/VMware-ESXi-8.0U3-24022510-depot.zip\u0026rdquo;\nesxcli software profile install \u0026ndash;depot=\u0026quot;/vmfs/volumes/esx04_local /VMware-ESXi-8.0U3-24022510-depot.zip\u0026quot; \u0026ndash;profile= ESXi-8.0U3-24022510-standard\nThis should run as shown on the picture above and after a host reboot you are ready for next steps.\n","date":"2025-01-08T16:06:49+01:00","permalink":"/p/esxi-cli-upgrade-from-v.8.02-gt/","title":"ESXi CLI upgrade from v.8.02-\u0026gt;"},{"content":"\nAs i quickly realized in part 1 and 2, the yaml file was incomplete and needed some different approach when asked for an output.\nOne thing was the many different templates you can create:\n1. VMware Infrastructure VMware vSphere: Direct support for deploying VMs and services on VMware\u0026rsquo;s core virtualization platform.\nVMware Cloud on AWS: Integration with VMware\u0026rsquo;s cloud offering on Amazon Web Services for seamless hybrid cloud operations.\nVMware NSX: Support for advanced network configurations and services using VMware’s network virtualization and security platform.\n2. Public Cloud Platforms Amazon Web Services (AWS): Comprehensive support for deploying and managing AWS resources such as EC2 instances, S3 storage, and more.\nMicrosoft Azure: Support for various Azure resources, including virtual machines, Azure SQL databases, and Azure Kubernetes Service.\nGoogle Cloud Platform (GCP): Allows for the deployment and management of Google Cloud resources like Compute Engine instances and Google Kubernetes Engine.\n3. Container Platforms Kubernetes: Native support for deploying and managing Kubernetes clusters and containerized applications across any infrastructure, including integrations with Tanzu Kubernetes Grid and other Kubernetes distributions.\nOpenShift: Can integrate with Red Hat OpenShift for managing applications and services on this enterprise Kubernetes platform.\n4. Private and Hybrid Clouds VMware Cloud Foundation: Support for deploying on VMware\u0026rsquo;s integrated private cloud platform that includes vSphere, vSAN, and NSX.\nVMware Cloud on Dell EMC: Allows businesses to manage and orchestrate environments running VMware’s cloud infrastructure on Dell EMC hardware.\n5. Other Integrations VMware Cloud Director (formerly vCloud Director): Supports deployments in environments managed by VMware Cloud Director, which is targeted at service providers and large enterprises managing multiple cloud instances. 6. Traditional IT Bare Metal Servers: Capability to manage and provision bare metal servers directly, expanding automation beyond virtual and cloud resources. 7. Edge Environments VMware Edge Compute Stack: Supports the deployment and management of edge computing scenarios where processing is done at or near the source of data generation. The list above shows that every blueprint has it`s prereq, and different config parameters, so you need to be very precise in your question for a template.\nThis means that i changed my approach a little and added more info to get a blueprint that i could use in my vsphere environment.\nMy first yaml created a cloud machine, but i needed a cloud vsphere machine, so that was one thing that needed to change. I also added info about the storage type and changed to win2022.\nMe:\n\u0026ldquo;Show me a aria automation cloud vsphere machine yaml file, for a windows 2022, with an existing c disk with 100gb. Storage type is cloud vsphere disk. disk number 2 is a DB disk with 300gb with letter E, disk number 3 is a log disk with 120gb with letter f and disk number 4 is a temp disk with 50gb with letter g.Disk provisioning type is thin. the vm uses NSX network called test2, and needs 2 cpu and 8gb ram\u0026rdquo;\nChatGPT:\nHere\u0026rsquo;s a VMware Aria Automation Cloud template that matches your specifications for a Windows Server 2022 vSphere machine using vSAN storage with a thin provisioning type:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 name: \u0026#34;Windows-Server-2022-VM-Detailed\u0026#34; description: \u0026#34;Provision a Windows Server 2022 VM with integrated cloud vSphere disk configuration.\u0026#34; version: \u0026#34;1.0\u0026#34; resources: windowsVm: type: Cloud.vSphere.Machine properties: name: \u0026#34;Win2022Server\u0026#34; image: \u0026#34;Windows-2022\u0026#34; flavor: \u0026#34;custom\u0026#34; cpuCount: 2 totalMemoryMB: 8192 networks: - networkType: \u0026#34;existing\u0026#34; networkName: \u0026#34;test2\u0026#34; storage: type: \u0026#34;cloud.vsphere.disk\u0026#34; datastore: \u0026#34;vsanDatastore\u0026#34; disks: - name: \u0026#34;C Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: 100 provisioningType: \u0026#34;thin\u0026#34; bootDisk: true - name: \u0026#34;DB Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: 300 deviceName: \u0026#34;E:\u0026#34; provisioningType: \u0026#34;thin\u0026#34; - name: \u0026#34;Log Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: 120 deviceName: \u0026#34;F:\u0026#34; provisioningType: \u0026#34;thin\u0026#34; - name: \u0026#34;Temp Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: 50 deviceName: \u0026#34;G:\u0026#34; provisioningType: \u0026#34;thin\u0026#34; customizationSpec: customizationType: \u0026#34;windows\u0026#34; timezone: \u0026#34;UTC\u0026#34; adminUsername: \u0026#34;administrator\u0026#34; adminPassword: \u0026#34;SecurePassword123!\u0026#34; computerName: \u0026#34;Win2022Server\u0026#34; preScript: inline: | # PowerShell script to initialize and format additional disks Get-Disk | Where-Object PartitionStyle -eq \u0026#39;RAW\u0026#39; | Initialize-Disk -PartitionStyle MBR -PassThru | ForEach-Object { $disk = $_ switch ($disk.Number) { 1 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;E\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;DBDisk\u0026#39; -Confirm:$false } 2 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;F\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;LogDisk\u0026#39; -Confirm:$false } 3 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;G\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;TempDisk\u0026#39; -Confirm:$false } } } Now we have a more complete file, but still we where missing some info. The blueprint has something called input parameters that is connected to the schema used when you request a vm from the Aria portal . Here you can setup variables like dropdown for different type of info, set disk size , name and so on, so its quite useful.\nNext step would be to ask for input parameters.\nMe:\n\u0026ldquo;Show me a aria automation cloud vsphere machine yaml file, for a windows 2022, with an existing c disk with 100gb. Storage type is cloud vsphere disk. disk number 2 is a DB disk with 300gb with letter E, disk number 3 is a log disk with 120gb with letter f and disk number 4 is a temp disk with 50gb with letter g.Disk provisioning type is thin. the vm uses NSX network called test2, and needs 2 cpu and 8gb ram , with template input parameters\u0026rdquo;\nChatGPT:\nHere\u0026rsquo;s a VMware Aria Automation Cloud template for a vSphere machine with template inputs as requested:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 name: \u0026#34;Deploy-Windows-2022-VM\u0026#34; description: \u0026#34;Template to deploy a Windows Server 2022 VM with custom disk configuration and NSX networking.\u0026#34; version: \u0026#34;1.0\u0026#34; inputs: vmName: type: string title: \u0026#34;Virtual Machine Name\u0026#34; description: \u0026#34;Name of the virtual machine to be deployed.\u0026#34; default: \u0026#34;Win2022VM\u0026#34; cpuCount: type: integer title: \u0026#34;CPU Count\u0026#34; description: \u0026#34;Number of CPUs for the virtual machine.\u0026#34; default: 2 memoryMB: type: integer title: \u0026#34;Memory in MB\u0026#34; description: \u0026#34;Amount of RAM in megabytes for the virtual machine.\u0026#34; default: 8192 networkName: type: string title: \u0026#34;NSX Network Name\u0026#34; description: \u0026#34;Name of the NSX network the VM will connect to.\u0026#34; default: \u0026#34;test2\u0026#34; adminUsername: type: string title: \u0026#34;Administrator Username\u0026#34; description: \u0026#34;Administrator username for the VM.\u0026#34; default: \u0026#34;admin\u0026#34; adminPassword: type: string title: \u0026#34;Administrator Password\u0026#34; description: \u0026#34;Administrator password for the VM.\u0026#34; encrypted: true dbDiskSizeGB: type: integer title: \u0026#34;DB Disk Size in GB\u0026#34; description: \u0026#34;Size of the database disk in gigabytes.\u0026#34; default: 300 logDiskSizeGB: type: integer title: \u0026#34;Log Disk Size in GB\u0026#34; description: \u0026#34;Size of the log disk in gigabytes.\u0026#34; default: 120 tempDiskSizeGB: type: integer title: \u0026#34;Temporary Disk Size in GB\u0026#34; description: \u0026#34;Size of the temporary disk in gigabytes.\u0026#34; default: 50 resources: windowsVm: type: Cloud.vSphere.Machine properties: name: \u0026#34;${input.vmName}\u0026#34; image: \u0026#34;Windows-2022\u0026#34; flavor: \u0026#34;custom\u0026#34; cpuCount: \u0026#34;${input.cpuCount}\u0026#34; totalMemoryMB: \u0026#34;${input.memoryMB}\u0026#34; networks: - networkType: \u0026#34;existing\u0026#34; networkName: \u0026#34;${input.networkName}\u0026#34; storage: type: \u0026#34;vsan\u0026#34; datastore: \u0026#34;vsanDatastore\u0026#34; disks: - name: \u0026#34;C Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: 100 provisioningType: \u0026#34;thin\u0026#34; bootDisk: true - name: \u0026#34;DB Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: \u0026#34;${input.dbDiskSizeGB}\u0026#34; deviceName: \u0026#34;E:\u0026#34; provisioningType: \u0026#34;thin\u0026#34; - name: \u0026#34;Log Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: \u0026#34;${input.logDiskSizeGB}\u0026#34; deviceName: \u0026#34;F:\u0026#34; provisioningType: \u0026#34;thin\u0026#34; - name: \u0026#34;Temp Disk\u0026#34; type: \u0026#34;Cloud.vSphere.Disk\u0026#34; capacityGB: \u0026#34;${input.tempDiskSizeGB}\u0026#34; deviceName: \u0026#34;G:\u0026#34; provisioningType: \u0026#34;thin\u0026#34; customizationSpec: customizationType: \u0026#34;windows\u0026#34; timezone: \u0026#34;UTC\u0026#34; adminUsername: \u0026#34;${input.adminUsername}\u0026#34; adminPassword: \u0026#34;${input.adminPassword}\u0026#34; computerName: \u0026#34;${input.vmName}\u0026#34; preScript: inline: | # PowerShell script to initialize and format additional disks Get-Disk | Where-Object PartitionStyle -eq \u0026#39;RAW\u0026#39; | Initialize-Disk -PartitionStyle MBR -PassThru | ForEach-Object { $disk = $_ switch ($disk.Number) { 1 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;E\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;DBDisk\u0026#39; -Confirm:$false } 2 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;F\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;LogDisk\u0026#39; -Confirm:$false } 3 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;G\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;TempDisk\u0026#39; -Confirm:$false } } } As you can see, the yaml file is starting to look a bit more complex, but also more complete. From the looks of it, i see that it still needs some adjusting. One thing is actually the disks itself, so to do a quick comparison, i created a new template manually in Aria, a cloud vsphere machine vm with 3 disks and nsx network connected.\nAfter a quick comparison of the yaml files i then merged the files , the manual template and the latest chatGPT template, and this gave me the most functioning blueprint in the test. There are some duplicate parameters, so it needs some adjusting, but i will test it along with some input parameters for the schema, so hopefully in the next post i will have a 100% functioning blueprint.\n","date":"2024-05-13T01:26:34+01:00","image":"/p/aria-automation-with-chatgpt-part-3/cover.png","permalink":"/p/aria-automation-with-chatgpt-part-3/","title":"Aria Automation with ChatGPT part 3"},{"content":"We talk about Tanzu, but what are the differences between a supervisor cluster and a TKG cluster?\nIn VMware\u0026rsquo;s Kubernetes ecosystem, a Supervisor Cluster and a Tanzu Kubernetes Grid (TKG) Cluster serve different but complementary roles. Here\u0026rsquo;s an overview of each and the key differences between them:\nVMware Supervisor Cluster Definition: A Supervisor Cluster is a Kubernetes cluster that runs directly on vSphere using ESXi as the worker nodes.\nIt integrates Kubernetes natively with vSphere through vSphere with Tanzu.\nArchitecture:\nRuns natively on ESXi, with each ESXi host serving as a Kubernetes worker node.\nUses vSphere Distributed Switch (vDS) and NSX-T for networking.\nIncorporates VMware vSphere Pod Service, allowing native Kubernetes Pods to run alongside VMs.\nFeatures:\nvSphere Pods: Lightweight pods that run directly on ESXi, providing isolation and security similar to VMs.\nNamespaces: Provide logical and security boundaries for resources within a vSphere environment.\nIntegrated Management: Manage through vCenter, leveraging vSphere roles and permissions.\nUse Case:\nIdeal for Kubernetes workloads that need direct integration with vSphere, particularly for users who want a tightly integrated Kubernetes solution within their existing VMware environment.\nTanzu Kubernetes Grid (TKG) Cluster Definition: TKG clusters are Kubernetes clusters managed and deployed by VMware Tanzu Kubernetes Grid.\nCan be deployed on multiple environments: vSphere, public clouds, and at the edge.\nArchitecture:\nTKG clusters run on top of the Supervisor Cluster, but also support standalone deployments.\nDeploys and manages clusters via Cluster API and Kubernetes Operators.\nFeatures:\nMulti-Cloud Support: Deploys across multiple cloud platforms like AWS, Azure, and vSphere.\nCluster API: Automates lifecycle management (creation, scaling, upgrade, and deletion) using Kubernetes-style declarative APIs.\nCompatible: Works with standard Kubernetes tooling.\nUse Case:\nBest for organizations seeking consistent Kubernetes deployments across hybrid environments (e.g., on-premises and public cloud).\nKey Differences Deployment Model: Supervisor Cluster: Kubernetes control plane runs directly on ESXi hosts.\nTKG Cluster: Kubernetes clusters deployed on top of the Supervisor Cluster or on other platforms.\nNetwork Integration:\nSupervisor Cluster: Integrates deeply with NSX-T and vDS for networking and security.\nTKG Cluster: Uses Calico for networking (NSX-T available for vSphere deployments).\nManagement Interface:\nSupervisor Cluster: Managed via vCenter.\nTKG Cluster: Managed via kubectl, Tanzu CLI, or through vCenter if deployed on a Supervisor Cluster.\nWorkload Types:\nSupervisor Cluster: Supports vSphere Pods and Tanzu Kubernetes Clusters.\nTKG Cluster: Standard Kubernetes clusters for portable workloads. Summary Supervisor Cluster provides native integration with vSphere and enables Kubernetes workloads to run directly on ESXi.\nTKG Cluster offers consistent Kubernetes clusters across multiple environments.\nTANZU Network choices:\nSo there is a network difference. This could be important in our design.\nWhat separates the different network options:\nNetworking Solutions in TKG Clusters Calico: Default Network Provider: In most TKG clusters, Calico is used as the default Container Network Interface (CNI).\nFeatures: Network Policy: Implements Kubernetes NetworkPolicy for fine-grained traffic control.\nIP Address Management: Manages pod IP addresses dynamically.\nOverlay Networking: Uses VXLAN or IP-in-IP encapsulation.\nAntrea:\nAlternative Network Provider: In certain TKG clusters, Antrea is available as an alternative CNI.\nFeatures: Open vSwitch (OVS) based networking.\nImplements Kubernetes NetworkPolicy.\nNSX-T Integration:\nAvailable for vSphere Deployments:\nWhen TKG clusters are deployed on vSphere with Tanzu (within Supervisor Clusters), NSX-T can be used as the network provider.\nFeatures: Networking and Security Policies: Provides centralized network security policies via NSX-T.\nLoad Balancer: Offers built-in load balancing.\nNetworking: Supports Tier-0 and Tier-1 routing.\nChoosing a Networking Solution Calico: Best suited for environments needing a simple and effective CNI solution.\nSupports a broad range of TKG deployments.\nAntrea:\nSuitable for users looking for an OVS-based solution.\nProvides efficient networking in TKG clusters.\nNSX-T:\nIdeal for environments requiring enterprise-grade networking and security.\nDeep integration with vSphere and enhanced capabilities.\nClarified Overview Supervisor Cluster (NSX-T or vDS): NSX-T and vDS are used to provide networking.\nSupervisor Cluster networks the TKG clusters deployed on top of it.\nTKG Cluster:\nDefault CNI: Uses Calico or Antrea by default.\nNSX-T Integration: Available only for TKG clusters on vSphere. Summary of Changes Clarification: NSX-T is not directly available in standalone TKG deployments but requires vSphere Supervisor Clusters.\nSo why would we have several TKG clusters in a single supervisor cluster?\n1. Multi-Tenancy Isolated Environments: Each TKG cluster can be allocated to different teams, departments, or tenants, ensuring that their resources, configurations, and security policies are isolated.\nAccess Control: Kubernetes RBAC can be applied independently within each TKG cluster, simplifying access management.\n2. Workload Segmentation Application Isolation: Different applications or microservices can be deployed in separate TKG clusters to minimize resource competition and security risks.\nEnvironment Segregation:\nDev/Test/Prod Environments: Keep development, testing, and production workloads separate to avoid cross-environment issues.\nCompliance: Ensure compliance by separating applications that require different security policies or standards. 3. Resource Management Scalability: Allows better resource allocation as each TKG cluster can scale independently.\nEnables efficient use of cluster resources based on workload needs.\nResource Quotas:\nEach TKG cluster can be configured with quotas for CPU, memory, storage, etc.\nPrevents one team or tenant from monopolizing resources. 4. Application Modernization Legacy and Modern Applications: Legacy applications requiring more control can be hosted in dedicated TKG clusters.\nModern, cloud-native applications can be placed in separate clusters with different networking or security requirements. 5. Networking Customization Network Policies: Different clusters can implement network policies using Calico or Antrea tailored to their specific requirements.\nNSX-T Integration: NSX-T policies can provide centralized networking and security policies for each TKG cluster.\n6. Disaster Recovery and High Availability Fault Isolation: Multiple TKG clusters within the Supervisor Cluster minimize the impact of a single cluster failure.\nBackup and Restore: Backup strategies can be specific to individual TKG clusters.\n7. Lifecycle Management Rolling Updates: Simplifies rolling updates since each TKG cluster can be updated independently.\nCluster API (CAPI): Cluster API facilitates efficient lifecycle management of multiple TKG clusters.\nSummary Deploying multiple TKG clusters within a single Supervisor Cluster provides flexibility, isolation, resource management, and scalability. Organizations can structure their Kubernetes environments according to business needs and application requirements while maintaining security and operational efficiency.\n","date":"2024-05-06T20:12:30+01:00","permalink":"/p/supervisor-cluster-vs-tkg-cluster-or-both/","title":"Supervisor cluster vs TKG cluster or both?"},{"content":"\nCreating a Cloud Template yaml file using chatgpt v.4.0:\nSo in previous post i tested the creation of a yaml file from ChatGPT. My first experience was that it was missing some info in the file, and that made me think i could benefit from trying a smarter AI, like the 4.0.\nI started like last time:\n**Me: **\nWrite me a aria automation cloud template, for a windows 2019, with a c disk with 100gb, and 3 additional disk. a DB disk with 300gb with letter E, a log disk with 120gb with letter F and a temp disk with 50gb with letter G. The vm uses NSX network, and needs 4 cpu and 16gb ram.\nChatGPT4:\nCreating an Aria Automation Cloud template involves defining the virtual machine specifications, including CPU, memory, storage, and network. Here is an example YAML file you could use:\nNotes: Replace Placeholders: Replace Windows-2019 with the actual image name in your cloud template.\nReplace NSX-Network with your actual NSX network name.\nDisk Device Letter:\nThe deviceName property specifies the drive letter. Ensure the disk initialization and formatting script handles the drive letter assignment correctly.\nFlavors: If custom flavors are not available, use a pre-defined flavor that matches the desired CPU and memory configuration.\nCloud-Init or PowerShell Customization To ensure the correct disk letters and formatting, include a customization script. You could use a PowerShell script like this:\nIntegrate the script into the YAML file:\nThe additional disks will be formatted and ready to use in Windows.\nHere\u0026rsquo;s a summary of how the customizationSpec works in this YAML template:\nPre-Script: Runs the provided PowerShell commands to initialize, partition, and format the additional disks.\nDrive Letter Assignment: Ensures each disk is assigned a specific drive letter as requested.\nAdjusted Template Here\u0026rsquo;s the adjusted template with a complete PowerShell script:\nNotes: PowerShell Script Logic: The script identifies uninitialized disks (PartitionStyle is RAW).\nInitializes them and assigns specific drive letters.\nFormatting Details:\nThe Format-Volume cmdlet creates an NTFS filesystem on each new partition.\nDisk Number Assumption: The script assumes that the additional disks will have numbers starting from 1.\nIf the disk numbers change due to configuration, you might need to adjust the switch statement. CODE:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 name: \u0026#34;windows-2019-template\u0026#34; description: \u0026#34;Windows Server 2019 with NSX network, 4 CPUs, 16GB RAM, 100GB C:, and 3 additional disks\u0026#34; version: \u0026#34;1.0\u0026#34; resources: windowsVm: type: Cloud.Machine properties: image: \u0026#34;Windows-2019\u0026#34; flavor: \u0026#34;custom\u0026#34; cpuCount: 4 totalMemoryMB: 16384 constraints: - tag: \u0026#34;os:windows\u0026#34; networks: - networkType: \u0026#34;existing\u0026#34; networkName: \u0026#34;NSX-Network\u0026#34; disks: - name: \u0026#34;C Disk\u0026#34; capacityGB: 100 bootDisk: true - name: \u0026#34;DB Disk\u0026#34; capacityGB: 300 deviceName: \u0026#34;E:\u0026#34; - name: \u0026#34;Log Disk\u0026#34; capacityGB: 120 deviceName: \u0026#34;F:\u0026#34; - name: \u0026#34;Temp Disk\u0026#34; capacityGB: 50 deviceName: \u0026#34;G:\u0026#34; customizationSpec: customizationType: \u0026#34;windows\u0026#34; preScript: inline: | # Initialize and format additional disks Get-Disk | Where-Object PartitionStyle -eq \u0026#39;RAW\u0026#39; | Initialize-Disk -PartitionStyle MBR -PassThru | ForEach-Object { $disk = $_ switch ($disk.Number) { 1 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;E\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;DBDisk\u0026#39; -Confirm:$false } 2 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;F\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;LogDisk\u0026#39; -Confirm:$false } 3 { New-Partition -DiskNumber $disk.Number -DriveLetter \u0026#39;G\u0026#39; -UseMaximumSize | Format-Volume -FileSystem NTFS -NewFileSystemLabel \u0026#39;TempDisk\u0026#39; -Confirm:$false } } } Well, i must say there`s quite a difference in the latest ChatGPT, in terms of logic and smartness in how to best set this up, and now we can see that the template is more complete, and with more settings it requires. But i still missed some settings, so i added another question:\nMe:\nAre there any other settings in the template we need ?\nChatGPT4:\nIn addition to the CPU, memory, disk, and network configuration already provided in the template, there are a few other important settings you might want to include:\n1. Security Settings Ensure Windows Firewall or other security configurations are in place.\n2. Domain Joining If the VM needs to join a domain, you can use the domainJoin property.\n3. Admin Credentials Specify admin credentials for access post-deployment.\n4. Time Zone Configuration Set the time zone if needed.\n5. NSX Advanced Settings In cases where NSX needs specific configurations, you might need additional settings.\nEnhanced Template Example Below is a more comprehensive template including the additional settings:\nExplanation of Additional Settings Domain Join domain: Domain to join (e.g., example.com).\ndomainUserName: Username with sufficient privileges to join the domain.\ndomainPassword: Password for the user.\nAdmin Credentials\nadminUsername: The username of the administrator account.\nadminPassword: The password for the administrator account.\nComputer Name\nAutomatically generates a unique computer name using the VM\u0026rsquo;s resource ID.\nTime Zone Specifies the Windows timezone setting (timezone property).\nSecurity Reminder Make sure sensitive information like passwords are handled securely and use secrets management where possible.\nSo here we got our answer. The output is as good as the question.\nI think i am in a better position to test the yaml file now, with all the extra settings and info. Hopefully it will work, but i am sure theres some need for a little adjusting. Its a start.\n","date":"2024-05-06T01:34:55+01:00","image":"/p/aria-automation-with-chatgpt-part-2/cover.png","permalink":"/p/aria-automation-with-chatgpt-part-2/","title":"Aria Automation with ChatGPT part 2"},{"content":"In another post about automation, i tested the creation of Cloud Templates in Aria Automation using ChatGPT.\nThis can also be done for the kubernetes yaml files.\nThis YAML file describes a TKG workload cluster configuration, using Cluster API (CAPI).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: name: my-tkg-cluster namespace: tkg-cluster-namespace spec: clusterNetwork: pods: cidrBlocks: - \u0026#34;192.168.0.0/16\u0026#34; services: cidrBlocks: - \u0026#34;10.96.0.0/12\u0026#34; controlPlaneRef: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlane name: my-tkg-control-plane infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: VSphereCluster name: my-vsphere-cluster --- apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlane metadata: name: my-tkg-control-plane namespace: tkg-cluster-namespace spec: version: v1.23.8 replicas: 3 infrastructureTemplate: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: VSphereMachineTemplate name: my-control-plane-template kubeadmConfigSpec: clusterConfiguration: apiServer: extraArgs: cloud-provider: external controllerManager: extraArgs: cloud-provider: external etcd: local: imageRepository: projects.registry.vmware.com/tkg imageTag: v3.5.3_vmware.1 initConfiguration: nodeRegistration: kubeletExtraArgs: cloud-provider: external joinConfiguration: nodeRegistration: kubeletExtraArgs: cloud-provider: external --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: VSphereMachineTemplate metadata: name: my-control-plane-template namespace: tkg-cluster-namespace spec: template: spec: cloneMode: linkedClone numCPUs: 2 memoryMiB: 8192 diskGiB: 50 network: devices: - networkName: \u0026#34;vm-network\u0026#34; dhcp4: true template: \u0026#34;ubuntu-2004-kube-v1.23.8\u0026#34; --- apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: name: my-tkg-worker-deployment namespace: tkg-cluster-namespace spec: clusterName: my-tkg-cluster replicas: 3 selector: matchLabels: cluster.x-k8s.io/cluster-name: my-tkg-cluster template: metadata: labels: cluster.x-k8s.io/cluster-name: my-tkg-cluster spec: version: v1.23.8 bootstrap: configRef: apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate name: my-worker-template infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: VSphereMachineTemplate name: my-worker-machine-template --- apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate metadata: name: my-worker-template namespace: tkg-cluster-namespace spec: template: spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: cloud-provider: external --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: VSphereMachineTemplate metadata: name: my-worker-machine-template namespace: tkg-cluster-namespace spec: template: spec: cloneMode: linkedClone numCPUs: 2 memoryMiB: 4096 diskGiB: 20 network: devices: - networkName: \u0026#34;vm-network\u0026#34; dhcp4: true template: \u0026#34;ubuntu-2004-kube-v1.23.8\u0026#34; Explanation:\napiVersion: Specifies the API version for the Tanzu Kubernetes Cluster resource.\nkind: Specifies the type of resource, in this case, TanzuKubernetesCluster.\nmetadata: Contains metadata about the cluster, such as its name.\nspec: Defines the specifications for the cluster.\ntopology: Specifies the cluster topology, including control plane and worker node configurations.\ncontrolPlane: Configuration for the control plane nodes.\ncount: Number of control plane nodes.\nclass: Resource class for control plane nodes (e.g., best-effort-small).\nstorageClass: Storage class for the control plane nodes (e.g., vsphere-with-kubernetes).\nworkers: Configuration for worker nodes.\ncount: Number of worker nodes.\nclass: Resource class for worker nodes (e.g., best-effort-small).\nstorageClass: Storage class for the worker nodes (e.g., vsphere-with-kubernetes).\nThis YAML configuration creates a Tanzu Kubernetes Cluster named \u0026ldquo;example-cluster\u0026rdquo; with one control plane node and three worker nodes. It uses the specified resource classes and storage classes for provisioning the nodes. Adjust the configuration according to your requirements and the specifications of your environment.\nExplanation of Key Elements Namespace: Ensure the **tkg-cluster-namespace** exists or create it.\nVSphereMachineTemplate:\nDefines the specifications for the control plane and worker node VMs.\nKubeadmControlPlane: Sets the control plane parameters like the Kubernetes version and replicas.\nMachineDeployment: Describes the worker node pool and its configuration.\nUsage Instructions Create the Namespace: 1 kubectl create namespace tkg-cluster-namespace Apply the YAML File: 1 kubectl apply -f my-tkg-cluster.yaml Monitor Cluster Creation: 1 kubectl get clusters -n tkg-cluster-namespace The Tanzu Management cluster: Creating a YAML configuration file for deploying a Tanzu Kubernetes Grid (TKG) cluster involves defining specifications for the Kubernetes cluster within a VMware environment. Below, I provide a sample YAML file to deploy a TKG management cluster and a workload cluster.\nExample YAML for TKG Management Cluster\nThe management cluster in Tanzu Kubernetes Grid acts as the central control plane through which workload clusters are deployed and managed. Here’s how you could define it:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # management-cluster-config.yaml --- CLUSTER_NAME: \u0026#34;tkg-management-cluster\u0026#34; CLUSTER_PLAN: \u0026#34;prod\u0026#34; INFRASTRUCTURE_PROVIDER: \u0026#34;vsphere\u0026#34; VSPHERE_USERNAME: \u0026#34;vsphere-user\u0026#34; VSPHERE_PASSWORD: \u0026#34;vsphere-password\u0026#34; VSPHERE_SERVER: \u0026#34;vsphere-server.example.com\u0026#34; VSPHERE_DATACENTER: \u0026#34;Datacenter\u0026#34; VSPHERE_DATASTORE: \u0026#34;vsphere-datastore\u0026#34; VSPHERE_NETWORK: \u0026#34;VM Network\u0026#34; VSPHERE_RESOURCE_POOL: \u0026#34;*/Resources\u0026#34; VSPHERE_FOLDER: \u0026#34;tkg-clusters\u0026#34; VSPHERE_SSH_AUTHORIZED_KEY: \u0026#34;ssh-rsa AAAA...\u0026#34; CONTROL_PLANE_MACHINE_TYPE: \u0026#34;medium\u0026#34; NODE_MACHINE_TYPE: \u0026#34;medium\u0026#34; NODE_MACHINE_COUNT: 3 KUBERNETES_VERSION: \u0026#34;v1.22.5+vmware.1-tkg.1-zshippable\u0026#34; This presumes Tanzu CLI is set up with the necessary plugins.\nthen run:\n1 tanzu management-cluster create --file management-cluster-config.yaml Example YAML for TKG Workload Cluster\nNow that you have a management cluster, you can deploy a workload cluster for your applications:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # workload-cluster-config.yaml --- CLUSTER_NAME: \u0026#34;tkg-workload-cluster-01\u0026#34; CLUSTER_PLAN: \u0026#34;dev\u0026#34; INFRASTRUCTURE_PROVIDER: \u0026#34;vsphere\u0026#34; NAMESPACE: \u0026#34;default\u0026#34; VSPHERE_USERNAME: \u0026#34;vsphere-user\u0026#34; VSPHERE_PASSWORD: \u0026#34;vsphere-password\u0026#34; VSPHERE_SERVER: \u0026#34;vsphere-server.example.com\u0026#34; VSPHERE_DATACENTER: \u0026#34;Datacenter\u0026#34; VSPHERE_DATASTORE: \u0026#34;vsphere-datastore\u0026#34; VSPHERE_NETWORK: \u0026#34;VM Network\u0026#34; VSPHERE_RESOURCE_POOL: \u0026#34;*/Resources\u0026#34; VSPHERE_FOLDER: \u0026#34;tkg-clusters\u0026#34; VSPHERE_SSH_AUTHORIZED_KEY: \u0026#34;ssh-rsa AAAA...\u0026#34; CONTROL_PLANE_MACHINE_TYPE: \u0026#34;small\u0026#34; WORKER_MACHINE_TYPE: \u0026#34;small\u0026#34; WORKER_MACHINE_COUNT: 3 KUBERNETES_VERSION: \u0026#34;v1.22.5+vmware.1-tkg.1-zshippable\u0026#34; and create with:\n1 tanzu cluster create --file workload-cluster-config.yaml ","date":"2024-05-02T09:45:42+01:00","permalink":"/p/deploying-tanzu-kubernetes-grid-with-yaml/","title":"Deploying Tanzu Kubernetes Grid with YAML"},{"content":"\nCreating a Cloud Template yaml file using chatgpt v.3.5:\nAria Automation Cloud Templates are based on yaml files and powershell scripts, as well as some Orchestrator Workflows.\nI was curious about using ChatGPT to create the basic config in the template, creating a cloud template *.yaml file and a powershell script for formatting the disks and set the blocksize, so i logged in and started with:\nMe:\n\u0026ldquo;create a aria automation cloud template for a windows2019 server with 4cpu, 12gb memory and 3 virtual disk with 100gb each\u0026rdquo;\nChatGPT:\nTo create an Aria Automation Cloud template for a Windows Server 2019 with the specified configuration (4 CPUs, 12GB memory, and 3 virtual disks of 100GB each), you would typically use a YAML configuration file. Below is an example YAML configuration that you can use:\nExplanation:\nname: Specifies the name of the template.\nversion: Specifies the version of the template.\ndescription: Provides a description of the template.\nresources: Defines the resources for the template.\nname: Specifies the name of the virtual machine.\ntype: Specifies the type of resource, in this case, a virtual machine (vm).\nimage: Specifies the image or operating system for the virtual machine, in this case, Windows Server 2019.\ncpus: Specifies the number of CPUs for the virtual machine.\nmemory: Specifies the memory size for the virtual machine.\ndisks: Specifies the virtual disks for the virtual machine, each with a size of 100GB.\nYou can save this configuration in a file with a .yaml extension, for example, windows2019_template.yaml. Then, you can use this YAML file to create the template in Aria Automation Cloud. Make sure to adjust the image name according to the available Windows Server 2019 image in your Aria Automation Cloud environment.\nMe:\nand can you add nsx network vlan203?\nChatGPT:\nTo add an NSX network with VLAN 203 to the Aria Automation Cloud template, you would typically include network configuration within the YAML file. Here\u0026rsquo;s an updated version of the previous YAML configuration that includes the NSX network VLAN 203:\nTo add an NSX network with VLAN 203 to the Aria Automation Cloud template, you would typically include network configuration within the YAML file. Here\u0026rsquo;s an updated version of the previous YAML configuration that includes the NSX network VLAN 203:\nMe:\nand can you say first disk 100gb disk letter c:, second disk 120gb disk letter E:, and third disk 200gb disk letter F: ?\nChatGPT:\nCertainly! To specify disk letters for each virtual disk in the Aria Automation Cloud template, you can use the disk_letter property. Here\u0026rsquo;s the updated YAML configuration:\nExplanation:\ndisks: Specifies the virtual disk configuration for the virtual machine. Each disk entry includes the size property for specifying the size of the disk.\nThe disk_letter property is used to assign the desired disk letter to each disk. With this configuration, the first disk will be assigned the letter C:, the second disk will be assigned E:, and the third disk will be assigned F:. You can adjust the disk sizes and letters according to your requirements. After updating the YAML file, use it to create the template in Aria Automation Cloud.\nMe:\nand are the disks formatted and ready to use?\nChatGPT:\nIn the context of the Aria Automation Cloud template, the disks specified in the configuration are typically presented to the virtual machine but are not automatically formatted or prepared for use.\nOnce the virtual machine is provisioned based on the template, it\u0026rsquo;s up to the user or administrator to perform any necessary disk management tasks, such as formatting the disks, creating partitions, and assigning drive letters within the guest operating system (in this case, Windows Server 2019).\nYou would typically use automation scripts, configuration management tools, or manual steps within the guest operating system to initialize and prepare the disks for use according to your specific requirements. This might involve using disk management tools within Windows to format the disks, create volumes, and assign drive letters as needed.\nMe:\ndo you have an example for a automation script that format the disks?\nChatGPT:\nSure, here\u0026rsquo;s an example PowerShell script that you can use to format disks and assign drive letters in Windows. This script assumes that the disks have already been presented to the virtual machine and you know their disk numbers (e.g., Disk 1, Disk 2, Disk 3).\nExplanation:\nThe script uses the Format-Volume cmdlet in PowerShell to format the volumes of the disks and assign drive letters.\nEach disk is formatted with the NTFS file system.\nThe -Confirm:$false parameter is used to suppress confirmation prompts.\nMake sure to adjust the $disk1Number, $disk2Number, and $disk3Number variables with the appropriate disk numbers assigned to the virtual disks in your environment. Also, adjust the $disk1Letter, $disk2Letter, and $disk3Letter variables to specify the desired drive letters for each disk.\nYou can save this script as a .ps1 file and then run it within the guest operating system of the virtual machine to format and assign drive letters to the disks. Make sure to run the script with appropriate permissions, typically as an administrator.\nMe:\nand can you add blocksize to that script?\nChatGPT:\nCertainly! You can specify the allocation unit size (block size) when formatting the volumes in Windows. Here\u0026rsquo;s the updated script with the block size specified:\nExplanation:\nThe $blockSize variable is set to specify the desired allocation unit size in bytes (e.g., 4096 for a 4KB allocation unit).\nThe -AllocationUnitSize parameter is added to the Format-Volume cmdlet to specify the block size for formatting the volumes.\nAdjust the $blockSize variable according to your requirements. The script will format the volumes of the disks with the specified block size and assign drive letters to them. Save the script as a .ps1 file and run it within the guest operating system of the virtual machine with appropriate permissions.\nThis shows us the many uses of ChatGPT and AI, and in this case giving us a quick start for creating our blueprint in Aria Automation.\n*Copy of the .yaml file:\nname: Windows2019_Template\nversion: 1\ndescription: Template for Windows Server 2019 with 4 CPUs, 12GB memory, 3 virtual disks of 100GB, 120GB, and 200GB each, NSX network VLAN 203, and disk letters assigned.\nresources:\n- name: windows2019_server\ntype: vm\nimage: windows2019\ncpus: 4\nmemory: 12GB\ndisks:\n- size: 100GB\ndisk_letter: C:\n- size: 120GB\ndisk_letter: E:\n- size: 200GB\ndisk_letter: F:\nnetworks:\n- name: vlan203_network\ntype: nsx\nvlan: 203\nCopy of the disk format powershell script:\nInitialize variables for disk numbers and drive letters $disk1Number = 1\n$disk2Number = 2\n$disk3Number = 3\n$disk1Letter = \u0026ldquo;C\u0026rdquo;\n$disk2Letter = \u0026ldquo;E\u0026rdquo;\n$disk3Letter = \u0026ldquo;F\u0026rdquo;\n$blockSize = 4096 # Specify the desired block size in bytes (e.g., 4096 for 4KB)\nFormat and assign drive letters to disks with specified block size Format-Volume -DriveLetter $disk1Letter -FileSystem NTFS -AllocationUnitSize $blockSize -Confirm:$false\nFormat-Volume -DriveLetter $disk2Letter -FileSystem NTFS -AllocationUnitSize $blockSize -Confirm:$false\nFormat-Volume -DriveLetter $disk3Letter -FileSystem NTFS -AllocationUnitSize $blockSize -Confirm:$false\nNew yaml and powershell:\nLooking at the config, i realized that i had to change the c disk, because the image will already have a C, so i changed the question a bit to include the existing disk:\nMe:\ncreate a aria automation cloud template for a windows2019 server with 4cpu, 12gb memory and 3 virtual disks, nsx network vlan 203, image has a c disk 100gb, add disk 2 letter d 100gb, disk 3 letter e 120gb ,disk 4 letter f 200gb\nChatGPT:\nBelow is the updated Aria Automation Cloud template YAML configuration based on your requirements:\nExplanation:\nThe template defines a Windows Server 2019 virtual machine with 4 CPUs and 12GB of memory.\nThe disks section includes the existing disk (C:) from the image and additional disks (D:, E:, F:) with their respective sizes and drive letters specified.\nThe NSX network with VLAN 203 is also included in the template configuration.\nEnsure to adjust the disk sizes, drive letters, and network details according to your requirements. Once you have saved this YAML configuration file, you can use it to create the template in Aria Automation Cloud.\nI then needed to update the powershell script:\nMe:\ndo you have an example for a automation script that format the disks with block size?\nChatGPT:\nCertainly!\nExplanation:\nThis script formats disks with drive letters D:, E:, and F: with the specified block size.\nIt uses the Format-Volume cmdlet to format the volumes of the disks and assigns the desired block size using the -AllocationUnitSize parameter.\nThe -Confirm:$false parameter suppresses confirmation prompts during the formatting process.\nMake sure to adjust the $disk2Letter, $disk3Letter, and $disk4Letter variables according to the drive letters you want to assign to the disks. Additionally, modify the $blockSize variable to specify the desired block size in bytes. Save this script as a .ps1 file and run it within the guest operating system of the virtual machine to format the disks with the specified block sizes and assign drive letters.\nAnd the new files:\n*Cloud template .yaml file:\nname: Windows2019_Template\nversion: 1\ndescription: Template for Windows Server 2019 with 4 CPUs, 12GB memory, NSX network VLAN 203, and additional disks with specific drive letters.\nresources:\n- name: windows2019_server\ntype: vm\nimage: windows2019\ncpus: 4\nmemory: 12GB\ndisks:\n- size: 100GB\ndisk_letter: C:\n- size: 100GB\ndisk_letter: D:\n- size: 120GB\ndisk_letter: E:\n- size: 200GB\ndisk_letter: F:\nnetworks:\n- name: vlan203_network\ntype: nsx\nvlan: 203\nAnd the powershell format disk script:\nformatdisk.ps\nDefine disk numbers and drive letters $disk1Letter = \u0026ldquo;C\u0026rdquo;\n$disk2Letter = \u0026ldquo;D\u0026rdquo;\n$disk3Letter = \u0026ldquo;E\u0026rdquo;\n$disk4Letter = \u0026ldquo;F\u0026rdquo;\n$blockSize = 4096 # Specify the desired block size in bytes (e.g., 4096 for 4KB)\nFormat and assign drive letters to disks with specified block size Format-Volume -DriveLetter $disk2Letter -FileSystem NTFS -AllocationUnitSize $blockSize -Confirm:$false\nFormat-Volume -DriveLetter $disk3Letter -FileSystem NTFS -AllocationUnitSize $blockSize -Confirm:$false\nFormat-Volume -DriveLetter $disk4Letter -FileSystem NTFS -AllocationUnitSize $blockSize -Confirm:$false\n\u0026ldquo;Automation frees humans from the mundane and repetitive, allowing us to focus on creativity, innovation, and problem-solving. It\u0026rsquo;s not about replacing people; it\u0026rsquo;s about empowering them to do more meaningful work.\u0026rdquo;\nNext post i will add the 2019 image to Aria Automation, and import the files to test and see if it works.\n","date":"2024-05-01T20:29:02+01:00","permalink":"/p/aria-automation-with-chatgpt-part-1/","title":"Aria Automation with ChatGPT part 1"},{"content":"With NSX we could use microsegmentation for the east-west traffic, and in a greenfield DC config, we could start with \u0026ldquo;zero trust\u0026rdquo; meaning all vms and ports are default deny or drop. Then you add rules opening up for the services you need, and this will get you only the ports necessary.\nTo do this in a brownfield, existing cluster means you cannot start with \u0026ldquo;Zero Trust\u0026rdquo;. Instead you start with adding tags, security groups, and security policies for specific applications, and these rules are not applied to the DFW, but to the security group. Within this group you create a deny all, and then you add new rules for the ports you need open. This way you only impact the vm`s in the group, and not everything else configured in the Distrubuted Firewall.\nThen when all vm`s are configured in groups with correct port and rules, you can activate zero trust in the DFW and adjust the deny rule in the groups. This way you slowly migrate into a zero trust config.\nTerminology \u0026amp; definitions:\n•Tags A virtual machine is not directly managed by NSX however, NSX allows the attachment of tags to a virtual machine. This tagging enables tag-based grouping of objects (e.g., you can apply a Tag called “AppServer” to all application servers).\n•Security Groups Security Groups enable you to assign security policies, such as distributed firewall rules, to a group of objects, such as virtual machines. In addition to Tags, you can also create groups based on VM attributes such as VM Name, OS, IP, Ports, etc.\n•Security Policies Each firewall rule contains policies that act as instructions that determine whether a packet should be allowed or blocked, which protocols it is allowed to use, which ports it is allowed to use, etc. Policies can be either stateful or stateless.\n","date":"2024-05-01T19:44:47+01:00","image":"/p/microsegmentation-zero-trust/microsegmentation.png","permalink":"/p/microsegmentation-zero-trust/","title":"Microsegmentation «Zero Trust»"},{"content":"Installing vcenter without DNS \u0026amp; NTP\nI recently had to install an offline install of vCenter, and this could give you some grey hair if you dont configure this the way vCenter wants to.\nAfter downloading the iso file you mount it and browse to the setup.exe file in the vcsa-ui-installer\nYou choose a basic install:\nGo through basic setup until:\nIn the Network setup, you have 2 specific settings that needs to change.\nthe FQDN needs to be IP address\nThe DNS server does not accept 127.0.0.1, so use your gateway\nWhen the first setup phase is done you are asked for NTP.\nif you don`t have this, use \u0026ldquo;sync with esxi host\u0026rdquo;\nThe rest should be okay, and hopefully you have a functioning vcenter after some time.\nMy experience was that VMware has tightened the DNS settings from vcenter 8.01 -\u0026gt; 8.02 , so latest version 8.0u1c was easier than u2.\nIf you have a some failed attempts like i did, you need to clean up your vsan config on your target host:\nCleanup vSAN Configuration\nssh to your ESXi host and do: esxcli vsan cluster leave.\ncheck the output of: esxcli vsan storage list and get the “VSAN Disk Group UUID”\ncontinue with: esxcli vsan storage remove -u Repeat on the second node.\nreboot node\nIf this does not work there are some excellent \u0026ldquo;small footprint\u0026rdquo; DNS servers like Bind and MaraDNS that you can install on a Linux server, like Ubuntu.\nBe sure to do the setup before you import the vm into the zone without internet and DNS. This is because Bind , MAKE and MaraDNS are not default packages in the standard linux image, but some distros have a full/complete image, so unless you have a proxy possibility to do a \u0026ldquo;apt-get install Bind\u0026rdquo;, use that image instead.\nDoing the config, export vm as *.ova, and import it to your dark site could be a solution.\nA small windows server2022 with DNS services is also a possibility, just to have a standalone mgmt DNS service for VMware, and here you can configure everything from the CD.\n","date":"2024-04-29T16:16:52+01:00","permalink":"/p/vcenter-dark-site-install/","title":"vCenter 'Dark site' install"},{"content":"The Aria Suite and the new licensing model from VMware by Broadcom.\nIn the VCF license, you get all the Aria products in Enterprise versions, meaning Aria Suite Enterprise.\nThis includes:\nLifecyclemanager\nVMware Identity manager (Workspace ONE)\nAria Automation (former vRealize Automation)\nAria Operations ( former vRealize Operations Manager)\nAria Operations for Logs ( former vRealize LogInsight)\nAria Operations for Networks (former vRealize Network Insight)\nIn the VVF license there is Aria Suite Standard.\nThis includes:\nLifecyclemanager\nAria Operations\nAria Operations for Logs\nSo what is the difference , and what is the value?\nThis means going from VVF license to VCF, apart from the obvious VCF functionality, also gives you all that the Lifecycle Suite Enterprise has to offer.\nWe know Operations Manager and Loginsight from earlier, but new centralized functionality in Lifecyclemanager, as well as Automation license and Network Insight, products that was quite pricey before this change is what makes this interresting.\nAria Automation integrates with all of the VCF functionality as well as support for Ansible, Terraform, Github, IPAM, Tanzu/kubernets and a lot more. Separate orchestrator engine for workflows and Cloud templates (blueprints) to easily create new applications from a central portal deploying either to your private or public cloud.Can be managed either as a service in VMware Cloud or in your datacenter.\nThe Network insight gives you almost instantly an understanding how your traffic flows in your datacenter by activation of netflow. You will see that you have maybe 90% east-west traffic, and only 10% North-South. It will also give source-destination with port info, either by vm or a group of vms, that will give you the basis of what rules to set for specific applications and help you set micro-segmentation rules in your distributed firewall. A valuable tool in your micro-segmentation project.\n","date":"2024-04-28T21:07:52+01:00","permalink":"/p/comparing-vmware-vcf-and-vvf-licensing-models/","title":"Comparing VMware VCF and VVF Licensing Models"},{"content":"\n1 node configuration:\nDuring setup of the first pod, i found out that the minimum config is exactly that, minimum. If your Holodeck host is configured 60+ cores, 1,5TB memory, and 12TB SSDdisk, no problem, but if not, what then?\nMgmt cluster w/Aria:\nYou have a pod ready to go, installed almost all the Aria products, Loginsight, Lifecycle manager, Identity manager, Operations manager and also Aria Automation. Only missing Network Insight.\n**Support vm`s and TKG: **\nYou deploy some ubuntu support vm`s for lab and testing according to the Holo guide, and also enable a Tanzu Kubernets Cluster.\nWorkload cluster:\nIf you now at this point want to deploy a 3-node workload cluster from SDDC manager, you are probably pretty close to maxing out the resources in your esxi server. The SDDC manager deploys a new vcenter, esxi`s for a vsan cluster, 3 NSX managers and an NSX Edge cluster containing 2 nodes, a supervisor cluster with 3 nodes, so yes, it will use some resources. You might decomission 2 NSX managers to free up some resources, but only after a successful deployment.\nAdditional vm`s:\nIf you now want to install or test something on top of what i just mentioned, it could be slow. So if you can,go for the recommended config.The Holo doc`s says 32cores, 1TB memory and 7TB SSD disk. And recommended config, is in my opinion 30+ cores, 512-756GB memory and 6-8TB ssd disk. Then you have more room for testing. I am not saying it will not work, just that you might experience latency in some cases if you want to try and enable all the SW and functionality.But it will work, no problem.\n\u0026ldquo;One of\u0026rdquo; my mistakes was deploying a workload cluster with the wrong disk parity, i used 1 instead of 0, so i overcommitted disk, and had to redeploy the Holodeck VCF build. The cloudbuilder got corrupt because i used more disk in the nested environment, than i had available in the physical host. Makes sense, really. And freeing up space didn’t help, the damage was done. I knew it took 3 hours to redeploy, but the troubleshooting, who knows?\n2-node configuration:\nSo to the initial idea, the 2-node config. If you dont have the upgrade possibility, but maybe another host just like the first one, you could set it up with basic config, meaning VVS and portgroup for Holosite1. This virtual switch is originally not connected to any physical port by design in the Holo config.\nBut\u0026hellip;., you can do a direct connect between them, and by doing so, extending the network to a secondary host with unused resources.\nYou will then need to map the physical ports to the Holodeck VVS called VLC-A or VLC-A2, depending on how you want to extend the resources. If you extend within your first pod to scale out with an extra Tanzu cluster or workload cluster, you use VLC-A, and if you want to setup a secondary pod, you use VLC-A2.\nThe direct connect physical nic`s should be 10Gb or higher to avoid any bottleneck when you extend the network. Using a Intel X520 10Gb SFP+ with a DAC cable was my choice for testing this, but it could be anything 10Gb-\u0026gt;, supported by VMware/ESXi.\nThe last scenario was actually connecting to a 10Gb switch using 3 servers, defining trunk ports on each side with 4095 on the VMware side of things and \u0026ldquo;allow vlan all\u0026rdquo; setting in your physical switch. This way you can extend with a workload cluster or Tanzu on secondary server using VVS(VMware Virtual Switch) VLC-A , and deploying a secondary pod on the third server to VLC-A2.\nNow you are ready to test:\nHCX ( Hybrid Cloud Extension):\n** SRM (Site Recovery Manager):**\nMultisite NSX with federation:\n","date":"2024-04-27T21:02:05+01:00","permalink":"/p/holodeck-with-2-physical-servers/","title":"Holodeck with 2 physical servers?"},{"content":"I have lately setup a few windows mgmt jumpserver vms, some temporary and some permanent, and when downloading the eval iso from windows, there is a specific process for changing the eval key, and setting a permanent key.This could be quite annoying if you dont change from eval to permanent key.\nThis is what worked for me:\nWindows Eval reg\nFirst , a separate product key is required for each Windows Server Evaluation version. Here are the public keys for Windows Server Evaluation 2019–2022 versions (standard and datacenter).\nWindows Server Evaluation 2019 Activation Key\nWindows Server201**9* Essentials ***JCKRF-N37P4-C2D82–9YXRT-4M63B\n***Windows Server 2019 Standard ***N69G4-B89J2–4G8F4-WWYCC-J464C\n***Windows Server 2019 ******Datacenter ***WMDGN-G9PQG-XVVXX-R3X43–63DFG\nWindows Server Evaluation 2022 Activation Key\nWindows Server***2022 Standard ***VDYBN-27WPP-V4HQT-9VMD4-VMK7H\nWindows Server 2022 Azure Edition* NTBV8–9K7Q8-V27C6-M2BTV-KHMXV*\n***Windows Server 2022 Datacenter ***WX4NM-KYWYW-QJJR4-XV3QB-6VM33\nCustomize the process according to your version. Open PowerShell as administrator. Then paste the public key with code below for the standard version and press enter.\nDISM /online /Set-Edition:ServerStandard /ProductKey:N69G4-B89J2–4G8F4-WWYCC-J464C /AcceptEula\nDISM /online /Set-Edition:ServerDatacenter /ProductKey:WMDGN-G9PQG-XVVXX-R3X43–63DFG /AcceptEula\nChange the public key in the **DISM **command according to your version. After the system does this, there will be a restart request, you can restart as Y/N. Press **Y **and Enter.\n","date":"2024-04-26T22:05:22+01:00","image":"/p/windows-server-eval-registration/cover.png","permalink":"/p/windows-server-eval-registration/","title":"Windows server Eval registration"},{"content":"I have another post about setting up the VMware HoloKit, but this post is about the HW needed.\nThe first test was a \u0026ldquo;lego-build\u0026rdquo;, a decommisioned Cisco server with memory from a Dell server, CPU`s from a Cisco blade, SSDdisks from another Cisco server, and so on, you get the picture. Eventually we had something that resembled a server with the minimum Holo prereq.\nAs the list below states, it`s important to have a supported cpu that works with the Cloudbuilder you are configuring. So make note of the cpu in your server, do a quick google search for: \u0026ldquo;CPU vmware hcl\u0026rdquo;, in my case : Intel e5-2620 v2 vmware hcl.\nIt will give you something like this:\nFrom the list above, it`s quite easy to confirm that our cpu in the Cisco server is only supported up until esxi v.7, so that means we need to consider that when setting up Holodeck.\nIf you take a look at the release notes for VCF and cloudbuilder 5.0 which is the recommended version in Holokit 2.0, you see a section called \u0026ldquo;Bill of materials BOM\u0026rdquo;:\nThis shows us every bit of SW that’s included in 5.0. Because this says esxi 8.0 update 1a, we can not use that version, and need to consider Cloudbuilder 4.5.2.\nHere the BOM looks like this:\nHere we have esxi version 7, so our cpu should be OK, but i can tell you, it`s a rocky road getting to the finish line with that version. So my tip before you start, verify your cpu, and try to get one certified for esxi8.\nYes , you can define: run unsupported mode(allowLegacyCPU=true) appending this setting during boot with \u0026ldquo;SHIFT+O\u0026rdquo; like William Lam has a post about, but when you during construct set up an NSX edge cluster, they will not start after deployment unless you configure the setting i mention below in your nested esxi server, trying to start the edge servers.\nThe esxi is actually a vm, so you need to add those 2 settings in adv. vmsettings. This is only for esxi7, unsupported CPU.\nThe Holodeck HW configurations:\n(Sorry about the Dell picture, it was taken by a colleague, he is a Consultant, not a photographer, obviously 😜)\nSo after struggling uphill for a while, i decided to make a HW change, and reconfigured a decommisioned Dell R730XD vsan hybrid node.\nI replaced the disk controller from passthrough to Raid, took half the cache disks in the hybrid node cluster, and setup a diskraid for Holodeck disk.\nThe CPU was Intel E5-2680 v4, meaning i had support for esxi v8, and was able to use the recommended Cloudbuilder in Holokit 2.0. This was completely different and the setup went smoothly the first time.\nThe lifecycle mangager and the Aria products are also tested with a specific version, so this also went smoothly on version 8, not so much on version 7.\nMy tip : go for version 8 with a supported CPU.\n","date":"2024-04-26T21:42:55+01:00","permalink":"/p/holodeck-hw/","title":"Holodeck HW"},{"content":"Changing VMware esxi machine certificates.\nI recently needed this, and the vmware documentation is describing a lot more steps than i think is needed. I had some nested esxi servers with the wrong hostname/IP, so i changed it, but i also needed to fix the machine certificate to reflect my recent change.\nvstellar.com has a great post about this:\nThe steps to generate a new ESXi host certificate are detailed here:\nStep 1. Log in to the ESXi shell as the root user.\nStep 2. Back up any existing certificates, just in case.\nmv /etc/vmware/ssl/rui.crt /etc/vmware/ssl/rui.crt.old mv /etc/vmware/ssl/rui.key /etc/vmware/ssl/rui.key.old NOTE: If the rui.crt and rui.key files do not exist then you do not need to back them up; you can just go to the next step.\nStep 3. Generate the new certificates: /sbin/generate-certificates Step 4. Reboot the ESXi host or restart the hostd process: /etc/init.d/hostd/restart Thats it! ","date":"2024-04-25T23:44:20+01:00","permalink":"/p/how-to-change-vmware-esxi-machine-certificates/","title":"How to Change VMware ESXi Machine Certificates"},{"content":"Creating your own private VCF lab\nSo, why would we use VMware Holodeck and not just setup basic VCF instead? This could be why:\nMinimum HW prerequisite for VCF are 4 nodes for the management domain, as well as a dedicated workload domain with a minimum of 3 hosts. Yes, you can use your management domain as workload domain, but if you don\u0026rsquo;t want to mix up mgmt and production, or for security reasons want to have this separate, then a split would be the way to go. If you don\u0026rsquo;t have 4 or 7 servers available in your lab, what then? Well, you could install Holodeck on ONE server instead with some nested esxi servers and vms..\nSo how do we do it? Well, you need som HW, and it`s based on some prerequisites for Holodeck, with a minimum config:\nMemory 384GB\nCPU 16Core (supported on HCL for esxi 8)\nDisk SSD, 3,5TB\nHere\u0026rsquo;s a full list:\nThen the deployment prerequisites:\nSo that\u0026rsquo;s a list! And how long does it take? what\u0026rsquo;s the timeframe?\nFrom the above picture and the prerequisites you can see that the timeframe expects a ready to go esxi from initial VLC deployment, so you can add some time, firmware-upgrading and configuring esxi 8.0 before you start. I would also go for creation of the VSS and port group in this step, no reason to wait for it.\nAs a tip I would also create a windows mgmtvm 2019-2022server for the first phase with at least 200gb C-disk where you download all the software. This is because in step 2 you configure the CreateISO.ps script with paths to all the software, and c:\\users\\administrator\\downloads is the default path in that script as well as a few others. Makes the process a bit easier and predictable. Running the script creates a 50 GB iso file with everything you need to create the Holo-Console VM. You need to copy the ISO to your esxi server and mount it in CD-ROM on your newly created vm Holo-Console. Let it boot from CD and wait til it finishes.\nThe holokit software comes with a ready to go *.ova file for the Holo-Router and this needs to be deployed to your esxi server. You add an external ip in CLI on the Holo-Router with SSH , and the router forwards port 3389 on this ip, so you can connect from your laptop with rdp and reach the Holo-Console VM on that exact address.\nHaving the Holo-Console vm as well as the Holo-Router vm, you are now ready to log into the Holo-Console vm and start the construct process.\nThe VLCGUI validates your input, so on your left you have:\nHolo-site1 pod script\nvlan, Holo uses vlan10\nYou can add an external DNS, so I used google\nYou need to map the cloud builder file\nSet a prefix\nInternal NTP and DNS which is 10.0.0.221, the address for the Cloudbuilder.\nThe domain\nAnd a *.json file containing info for the workload domain.\nOn your right you have:\nVcenter or esxi server\nuser and password\nmapping for cluster, network and disk\nIn the bottom you can choose to:\nAdd an NSX edge cluster\nSupervisor cluster\nAVN, NSX segments/portgroups for Lifecycle manager vms\nIf you get all green under validation, you press construct and this starts the automated process.\nFor me this took about 3.5 hours, and during this process you can watch the individual tasks in the powershell or you point your browser to https://10.0.0.221/bringup to watch it there as well. This is available after the cloudbuilder is created, and an initial task in the construct process. Give it about 30 min, and it\u0026rsquo;s probably ready to watch. If you where setting up a basic VCF solution, this would be the place to watch the process during installation as well.\nAfter a successful deployment you have a fully configured SDDC environment \u0026ldquo;ready to go\u0026rdquo;. But what does that mean actually? Lets take a look.\nNow you have:\nSDDC manager\n4-node vSphere mgmt cluster\nFully configured software defined storage with vSan\nFully configured software defined network with NSX\n\u0026ldquo;a ready to go\u0026rdquo; supervisor cluster to run Kubernetes\nAria LCM,IDM,vRA,vRNI,vRLI,vROPS in Enterprise editions\n(all the Aria products needs to be installed separately after deploy)\nSDDC manager dashboard where all the magic happens:\nIn Holodeck 5.0 you need to manually install the Lifecyclemanager with IDM and Aria Automation with an easy setup iso file.\nIn future release of holodeck i guess it would reflect the basic VCF installation where this was said to be an automated process, but in 5.1 this is still a manual process. Next version maybe\u0026hellip;\nImportant Holodeck links:\nHolo Overview:\nhttps://core.vmware.com/resource/holo-toolkit-20-overview\nHolo Introduction:\nhttps://core.vmware.com/introducing-holodeck-toolkit?\nDownload Holokit:\nhttps://via.vmw.com/Get-Holo-Toolkit\nHolo Setup:\nhttps://core.vmware.com/cloud-foundation-holodeck-20-setup\n","date":"2024-04-25T22:44:27+01:00","image":"/p/vmware-holodeck/cover.png","permalink":"/p/vmware-holodeck/","title":"VMware Holodeck"},{"content":"Vidar Berg-Ingebrigtsen is an Advisor / Architect working with virtualization and VMware , in Hybrid-Cloud division in Atea, Oslo\nMain focus has been NSX, vSAN, as well as Aria automation and VMware EUC/Horizon, but lately also VCF and Tanzu / K8s.\n","date":"2024-04-25T12:07:33+01:00","permalink":"/p/about/","title":"About"},{"content":"For more details, check out the documentation.\nBilibili video Tencent video YouTube video Generic video file Your browser doesn't support HTML5 video. Here is a link to the video instead. GitLab Quote Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n― A famous person, The book they wrote Photo by Codioful on Unsplash\n","date":"2023-08-25T00:00:00Z","image":"/p/shortcodes/cover.jpg","permalink":"/p/shortcodes/","title":"Shortcodes"}]