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://retouw.eu # #------------------------------------------------- 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*"){ $sa.profile=@() } } } 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[]]$dnsarray=@($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.