If you are like me, you are these days trying to automate as much as possible. It is just the best way to go since it makes it much easier to be consistent and uniform across the board. If you have a new VMware ESXi cluster you are standing up, creating a PowerCLI setup script is a great way to automatically create VLANs, VMkernel ports, and iSCSI configurations.
PowerCLI to the rescue
In case you didn’t know, VMware PowerCLI is the VMware-specific PowerShell module that allows you to interact with the APIs on a VMware ESXi host directly or a vCenter Server. Using PowerCLI, you can automate just about anything you want, including creating your port groups, VLANs, VMkernel ports, and iSCSI configuration.
Generic VMware ESXi PowerCLI Setup Script for Port groups, VLANs, VMkernel ports, and iSCSI
Let’s take a look at a generic script that will allow you to pluin your ESXi hosts in your vSphere cluster, and then populate these with the information needed for configuring things like port groups, VLAN tags, VMkernel ports, and iSCSI software adapter, etc.
The sections are pretty explanatory with the comments, but below is a high-level overview:
- Define the hostnames and IP addresses for things like the vMotion networks, iSCSI networks
- Define which vmnics are backing the vSwitch0 configuration and setting these to active
- Create a new vSwitch1 for vMotion01 and iSCSI01 networks
- Create a new vSwitch2 for vMotion02 and iSCSI02 networks
- Set jumbo frames where needed
- Set up NTP to point to an NTP server
- Add the iSCSI Software Adapter
# Define hostnames and IPs with the updated network configuration
$esxiHosts = @(
@{Name = "esxiHost01.local"; vMotion01 = "192.168.1.1"; vMotion02 = "192.168.2.1"; iSCSI01 = "172.16.1.1"; iSCSI02 = "172.16.2.1"},
@{Name = "esxiHost02.local"; vMotion01 = "192.168.1.2"; vMotion02 = "192.168.2.2"; iSCSI01 = "172.16.1.2"; iSCSI02 = "172.16.2.2"},
@{Name = "esxiHost03.local"; vMotion01 = "192.168.1.3"; vMotion02 = "192.168.2.3"; iSCSI01 = "172.16.1.3"; iSCSI02 = "172.16.2.3"}
)
foreach ($esxiHostConfig in $esxiHosts) {
$esxHost = Get-VMHost -Name $esxiHostConfig.Name
# Ensure vSwitch0 exists
$vSwitch0 = Get-VirtualSwitch -VMHost $esxHost -Name "vSwitch0" -ErrorAction SilentlyContinue
if ($vSwitch0 -eq $null) {
# If vSwitch0 doesn't exist, create it with vmnic2 and vmnic4
$vSwitch0 = New-VirtualSwitch -VMHost $esxHost -Name "vSwitch0" -Nic "vmnic2","vmnic4"
} else {
# Add vmnic2 and vmnic4 to vSwitch0 if they are not already present
$currentNics = $vSwitch0.Nic
$requiredNics = @("vmnic2", "vmnic4")
$missingNics = $requiredNics | Where-Object { $_ -notin $currentNics }
foreach ($nic in $missingNics) {
$nicObject = Get-VMHostNetworkAdapter -VMHost $esxHost -Name $nic -Physical
if ($nicObject) {
Add-VirtualSwitchPhysicalNetworkAdapter -VirtualSwitch $vSwitch0 -VMHostPhysicalNic $nicObject
}
}
}
# Ensure NIC teaming policy is correctly set for vSwitch0
Get-VirtualPortGroup -VMHost $esxHost -VirtualSwitch $vSwitch0 | Get-NicTeamingPolicy | Set-NicTeamingPolicy -MakeNicActive "vmnic2","vmnic4" -MakeNicUnused "vmnic0" -FailbackEnabled:$false
# Add Port Groups to vSwitch0 with VLAN tags for VM connectivity
$portGroups = @(
@{Name = "Mgmt-VLAN10"; VlanId = 10},
@{Name = "App-VLAN20"; VlanId = 20},
@{Name = "DB-VLAN30"; VlanId = 30},
@{Name = "Web-VLAN40"; VlanId = 40},
@{Name = "Backup-VLAN50"; VlanId = 50},
@{Name = "Storage-VLAN60"; VlanId = 60}
)
foreach ($pg in $portGroups) {
if (-not (Get-VirtualPortGroup -VMHost $esxHost -Name $pg.Name -ErrorAction SilentlyContinue)) {
New-VirtualPortGroup -VirtualSwitch $vSwitch0 -Name $pg.Name -VLanId $pg.VlanId
}
}
# Create vSwitch1 for iSCSI01 and vMotion01 with jumbo frames backed by vmnic3
$vSwitch1 = Get-VirtualSwitch -VMHost $esxHost -Name "vSwitch1" -ErrorAction SilentlyContinue
if ($vSwitch1 -eq $null) {
$vSwitch1 = New-VirtualSwitch -VMHost $esxHost -Name "vSwitch1" -Nic "vmnic3" -Mtu 9000
}
# Create Port Groups on vSwitch1 without VLAN tags (access ports)
if (-not (Get-VirtualPortGroup -VMHost $esxHost -Name "vmotion01" -ErrorAction SilentlyContinue)) {
New-VirtualPortGroup -VirtualSwitch $vSwitch1 -Name "vmotion01"
}
if (-not (Get-VirtualPortGroup -VMHost $esxHost -Name "iscsi01" -ErrorAction SilentlyContinue)) {
New-VirtualPortGroup -VirtualSwitch $vSwitch1 -Name "iscsi01"
}
# Create VMKernel Ports on vSwitch1 (Corrected VirtualSwitch parameter)
New-VMHostNetworkAdapter -PortGroup "vmotion01" -VirtualSwitch $vSwitch1.Name -IP $esxiHostConfig.vMotion01 -SubnetMask 255.255.255.0 -VMHost $esxHost -Mtu 9000
New-VMHostNetworkAdapter -PortGroup "iscsi01" -VirtualSwitch $vSwitch1.Name -IP $esxiHostConfig.iSCSI01 -SubnetMask 255.255.255.0 -VMHost $esxHost -Mtu 9000
# Create vSwitch2 for iSCSI02 and vMotion02 with jumbo frames backed by vmnic5
$vSwitch2 = Get-VirtualSwitch -VMHost $esxHost -Name "vSwitch2" -ErrorAction SilentlyContinue
if ($vSwitch2 -eq $null) {
$vSwitch2 = New-VirtualSwitch -VMHost $esxHost -Name "vSwitch2" -Nic "vmnic5" -Mtu 9000
}
# Create Port Groups on vSwitch2 without VLAN tags (access ports)
if (-not (Get-VirtualPortGroup -VMHost $esxHost -Name "vmotion02" -ErrorAction SilentlyContinue)) {
New-VirtualPortGroup -VirtualSwitch $vSwitch2 -Name "vmotion02"
}
if (-not (Get-VirtualPortGroup -VMHost $esxHost -Name "iscsi02" -ErrorAction SilentlyContinue)) {
New-VirtualPortGroup -VirtualSwitch $vSwitch2 -Name "iscsi02"
}
# Create VMKernel Ports on vSwitch2 (Corrected VirtualSwitch parameter)
New-VMHostNetworkAdapter -PortGroup "vmotion02" -VirtualSwitch $vSwitch2.Name -IP $esxiHostConfig.vMotion02 -SubnetMask 255.255.255.0 -VMHost $esxHost -Mtu 9000
New-VMHostNetworkAdapter -PortGroup "iscsi02" -VirtualSwitch $vSwitch2.Name -IP $esxiHostConfig.iSCSI02 -SubnetMask 255.255.255.0 -VMHost $esxHost -Mtu 9000
# Remove vmnic0 from vSwitch0
if ($currentNics -contains "vmnic0") {
Get-VMhost $esxHost | Get-VMHostNetworkAdapter -Physical -Name "vmnic0" | Remove-VirtualSwitchPhysicalNetworkAdapter -Confirm:$false
}
# Configure NTP
$ntpServers = "pool.ntp.org"
$ntpConfig = Get-VMHostNtpServer -VMHost $esxHost
if ($ntpConfig) {
Remove-VMHostNtpServer -VMHost $esxHost -NtpServer $ntpConfig -Confirm:$false
}
Add-VMHostNtpServer -VMHost $esxHost -NtpServer $ntpServers
Get-VMHostService -VMHost $esxHost | Where-Object { $_.Key -eq "ntpd" } | Start-VMHostService
Get-VMHostService -VMHost $esxHost | Where-Object { $_.Key -eq "ntpd" } | Set-VMHostService -Policy "on"
# Enable Software iSCSI Adapter
Get-VMHostStorage -VMHost $esxHost | Set-VMHostStorage -SoftwareIScsiEnabled $true
Write-Host "iSCSI Software Adapter enabled on host: $($esxiHostConfig.Name)"
# Retrieve iSCSI HBA name
$iscsiHBA = Get-VMHostHba -VMHost $esxHost -Type iScsi | Where-Object { $_.Model -eq "iSCSI Software Adapter" }
}
Adding a section to add your iSCSI bindings as well
If you want to add your iSCSI bindings to your cluster hosts, you can do that with something like the following code:
foreach ($hostConfig in $hosts) {
$vmHost = Get-VMHost -Name $hostConfig.Name
# Enable Software iSCSI Adapter
Get-VMHostStorage -VMHost $vmHost | Set-VMHostStorage -SoftwareIScsiEnabled $true
Write-Host "iSCSI Software Adapter enabled on host: $($hostConfig.Name)"
# Retrieve iSCSI HBA name
$iscsiHBA = Get-VMHostHba -VMHost $vmHost -Type iScsi | Where-Object { $_.Model -eq "iSCSI Software Adapter" }
if ($iscsiHBA) {
$iScsiHbaName = $iscsiHBA.Device
# Sets up PowerCLI to be able to access esxcli commands
$esxcli = Get-EsxCli -VMHost $vmHost -V2
try {
# Binds VMKernel ports created earlier to the iSCSI Software Adapter HBA
$bindParams1 = @{
adapter = $iScsiHbaName
nic = "vmk2" # Replace with the appropriate VMkernel adapter name
force = $true
}
$esxcli.iscsi.networkportal.add.Invoke($bindParams1)
Write-Host "VMKernel port 'vmk2' bound to iSCSI Software Adapter on host: $($hostConfig.Name)"
} catch {
Write-Host "Error binding VMKernel port 'vmk2' on host $($hostConfig.Name): $_"
}
try {
# Binds another VMKernel port to the iSCSI Software Adapter HBA
$bindParams2 = @{
adapter = $iScsiHbaName
nic = "vmk4" # Replace with the appropriate VMkernel adapter name
force = $true
}
$esxcli.iscsi.networkportal.add.Invoke($bindParams2)
Write-Host "VMKernel port 'vmk4' bound to iSCSI Software Adapter on host: $($hostConfig.Name)"
} catch {
Write-Host "Error binding VMKernel port 'vmk4' on host $($hostConfig.Name): $_"
}
} else {
Write-Host "No iSCSI Software Adapter found on host: $($hostConfig.Name)"
}
}
Wrapping up
As you can see with the code we have demonstrated here, you can take basically configured ESXik hosts outside of having a hostname and IP address for connectivity in vCenter server, and then configure these with port groups, vSwitches, VLAN tags, jumbo frames, iSCSI storage adapter, NTP configuration, and even add the actual iSCSI bindings to each server. Pretty cool. Let me know if you have something similar worked up for your environment.