First of all: I love Host Profiles! But they’re easy to mess up as well, leave something selected related to hardware and an update in ESXi, vib’s or even a firmware update might break it. For a customer where we are going to do the entire vSphere build from scratch I got the idea to generate an empty Host Profile and extend that one using scripting. At first I though this would be an easy thing but it definitely isn’t, a reply from PowerCLI guru Luc Dekens at the VMware{Code} forums set me on the right path to do so. Luc’s remark that editing Host Profiles might take some reverse engineering for the lack of documentation is a huge understatement. It has cost me many many hours to build the script below.

I strongly recommend having the reference host as clean as possible.

These are the steps the script takes

connect to vCenter extract a new Host Profile Gets the new Host Profile Copies all members of the new Host Profile to an object that can be edited Sets everything that I could find in my environment to false Updates the Host Profile with the edited object

Required parameters

vCenter Your vCenter host

Referencehost the name of the host in vCenter

Hostprofilename Name for the Host Profile



There are also a couple of optional parameters:

dnshost It’s mandatory to have a DNS set in the defaulttcpipstack. With this parameter you can change this.

domainname Like DNS it’s mandatory to have a domainname set in the defaulttcpipstack. With this parameter you can change this

Cleanup This one defaults to false but can be set to true. It will remove all NFS Datastores, vmkernel ports, portgroups, device aliases and direct i/o profiles. Use this one with care, if you apply it to a host it will most probably remove all networking details for that host making it unusable.



This is how a manual extracted Host Profile looks

This is how a Host Profile looks after using my script without the cleanup option, everything is deselected but the device aliases for example are kept.

.\create_clean_hostprofile.ps1 -vcenter vCenter -Hostprofilename demo_no_cleanup -referencehost hostname

And this is how it looks with the cleanup used.

.\create_clean_hostprofile.ps1 -vcenter vCenter -Hostprofilename demo_no_cleanup -referencehost hostname -cleanup $true

The script itself can be found on Github as well:

#------------------------------------------------- # Generates a clean Host Profile # # Build using PowerCLI 11 # # Version 1.0 # 17-08-2019 # Created by: Wouter Kursten # Website: https://www.retouw.nl # #------------------------------------------------- param( [Parameter(Mandatory=$true)][String]$Hostprofilename, [Parameter(Mandatory=$true)][String]$vcenter, [Parameter(Mandatory=$true)][String]$referencehost, [Parameter()][String]$dnshost, [Parameter()][String]$domainname, [Parameter()][bool]$Cleanup = $false ) # I grabbed this function somewhere from an example by Luc Dekens function Copy-Property ($From, $To, $PropertyName ="*"){ foreach ($p in Get-Member -In $From -MemberType Property -Name $propertyName){ trap { Add-Member -In $To -MemberType NoteProperty -Name $p.Name -Value $From.$($p.Name) -Force continue } $To.$($P.Name) = $From.$($P.Name) } } #connect to the vCenter connect-viserver $vcenter # This deletes any existing Host Profile with the same name as we're using in this script get-vmhostprofile -name $Hostprofilename -ErrorAction SilentlyContinue | Remove-VMHostProfile -Confirm:$false # This creates a new Host Profile from the referencehost new-vmhostprofile -name $Hostprofilename -referencehost $referencehost # Retrieves the newly created Host Profile $hp = Get-VMHostProfile -Name $Hostprofilename # Creates the spec where the cleanup is done $spec = New-Object VMware.Vim.HostProfileCompleteConfigSpec # Copies all properties of the new Host Profile to the spec Copy-Property -From $hp.ExtensionData.Config -To $spec # This removes everything that could be specific to the referencehost if ($cleanup -eq $true){ $spec.ApplyProfile.Network.Vswitch=$null $spec.ApplyProfile.Network.VMportgroup=$null $spec.ApplyProfile.Network.HostPortGroup=$null $spec.ApplyProfile.Network.pnic=$null $spec.ApplyProfile.Storage.NasStorage=$null ($spec.ApplyProfile.Property | where-object {$_.PropertyName -like "*DeviceAlias*"}).profile=$null ($spec.ApplyProfile.Property | where-object {$_.PropertyName -like "*PCI*"}).profile.property.profile=$null } # From here it's just disabling of items except for: # -items under storage> PSA Configuration (profiles are removed) # -Properties of the fixed DNS config (set to the default values from this scripts parameters) $spec.ApplyProfile.Datetime.Enabled=$False $spec.ApplyProfile.Authentication.Enabled=$False $spec.ApplyProfile.Authentication.ActiveDirectory.Enabled=$False foreach ($o in $spec.applyprofile.Option){ if ($o.Enabled){ $o.Enabled=$False } } foreach ($p in $spec.ApplyProfile.Property.Profile){ if ($p.Enabled){ $p.Enabled=$False } foreach ($pa in $p.Property.Profile){ if ($pa.Enabled){ $pa.Enabled=$False } foreach ($paa in $pa.Property.Profile){ if ($paa.Enabled){ $paa.Enabled=$False } } } } foreach ($s in $spec.ApplyProfile.Storage.Nasstorage){ if ($s.Enabled){ $s.Enabled=$False } foreach ($sa in $s){ if ($sa.Enabled){ $sa.Enabled=$False } } } foreach ($s in $spec.ApplyProfile.Storage.Property.Profile){ if ($s.Enabled){ $s.Enabled=$False } if ($s.ProfileTypeName -eq "psa_psaProfile_PluggableStorageArchitectureProfile" -AND $cleanup -eq $true){ foreach ($sa in $s.property){ if ($sa.propertyname -like "*psa_psaProfile_PsaDevice*"){ [email protected]() } } } foreach ($sa in $s.Property.Profile){ if ($sa.Enabled){ $sa.Enabled=$False } foreach ($saa in $sa.Property.Profile){ if ($saa.Enabled){ $saa.Enabled=$False } } } } foreach ($f in $spec.ApplyProfile.Firewall.ruleset){ if ($f.Enabled){ $f.Enabled=$False } } foreach ($n in $spec.ApplyProfile.Network.vswitch){ if ($n.Enabled){ $n.Enabled=$False } foreach ($na in $n){ if ($na.Enabled){ $na.Enabled=$False } foreach ($naa in $na.link){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.NumPorts){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.NetworkPolicy){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } } } foreach ($n in $spec.ApplyProfile.Network.pnic){ if ($n.Enabled){ $n.Enabled=$False } foreach ($na in $n){ if ($na.Enabled){ $na.Enabled=$False } } } foreach ($n in $spec.ApplyProfile.Network.VmPortGroup){ if ($n.Enabled){ $n.Enabled=$False } foreach ($na in $n){ if ($na.Enabled){ $na.Enabled=$False } foreach ($naa in $na.Vlan){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.Vswitch){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.NetworkPolicy){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } } } foreach ($n in $spec.ApplyProfile.Network.HostPortGroup){ if ($n.Enabled){ $n.Enabled=$False } foreach ($na in $n){ if ($na.Enabled){ $na.Enabled=$False } foreach ($naa in $na.IpConfig){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.Vlan){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.Vswitch){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } foreach ($naa in $na.NetworkPolicy){ if ($naa.enabled -eq $True){ $naa.Enabled=$False } } } } foreach ($n in $spec.ApplyProfile.Network.Property.Profile){ if ($n.Enabled){ $n.Enabled=$False } foreach ($na in $n.Property.Profile){ if ($na.Enabled){ $na.Enabled=$False } foreach ($np in $na.policy.policyoption){ if ($np.id -eq "FixedDnsConfig"){ foreach ($npp in $np.parameter){ if ($dnshost){ if ($npp.key -eq "address") { [string[]][email protected]($dnshost) $npp.value=$dnsarray } } if ($domainname){ if ($npp.key -eq "domainName"){ $npp.value=$domainname } } } } } foreach ($naa in $na.Property.Profile){ if ($naa.Enabled){ $naa.Enabled=$False } foreach ($naaa in $naa.Property.Profile){ if ($naaa.Enabled){ $naaa.Enabled=$False } } } } } (Get-VMHostProfile $Hostprofilename).ExtensionData.Updatehostprofile($spec) disconnect-viserver $vcenter -confirm:$False

And yes that’s a lot of foreach’s.