#!/usr/bin/pwsh
#
#(c) 2023 JetStream Software, Inc < legal @jetstreamsoft.com >
#
# This software is provided under the terms of the
# JetStream Software Inc.Software Tools License Agreement(STLA).
# Use of this software constitutes agreement to the terms of the STLA.
#
# force stop on error
$ErrorActionPreference = "Stop"
# vCenter parameters - fill with caution!
$login = "[email protected]"
$passwd = ""
$srvaddr = "10.40.4.101"
# vCenter FQDN
$drva_prefix = "jss-"
# Expect 4.2 / 4.1 .18 compatible extra routing by
default.
class netConfig {
[string] $netName
[string] $routes[string] $gw[string] $setup[string] $dns
}
# rules to fill the configuration:
# - only one interface is
default (others are non -
default)
# - All interfaces must exist on the appliance
$fullConfig = @(
#[netConfig] @ {
netName = "VM Network";
gw = "*";
setup = "";
routes = "10.8.0.0/24 via 10.50.0.11";
dns = "8.8.8.8, 1.1.1.1"
},
#[netConfig] @ {
netName = "VM Network 2";
gw = "";
setup = "";
routes = "10.20.0.0/24 via 192.168.100.2";
dns = ""
},
#[netConfig] @ {
netName = "VM Network 3";
gw = "";
setup = "";
routes = "10.12.0.0/24 via 10.5.5.2 metric 500,10.10.0.0/24 via 10.5.5.2";
dns = ""
}
[netConfig] @ {
netName = "VM Network";
gw = "10.40.0.1";
setup = "10.40.2.25#255.255.248.0";
routes = "";
dns = "10.40.0.9"
}
#[netConfig] @ {
netName = "VM Network";
gw = "";
setup = "";
routes = "";
dns = ""
},
#[netConfig] @ {
netName = "VM Network 2";
gw = "192.168.100.1";
setup = "192.168.100.99#255.255.255.0";
routes = "";
dns = ""
},
#[netConfig] @ {
netName = "VM Network 3";
gw = "";
setup = "";
routes = "10.10.0.0/24 via 10.5.5.2 metric 666";
dns = ""
}
)
function check_ip {
param(
[string] $ipv4_str
)
$parts = $ipv4_str.Split('.')
if ($parts.Length - ne 4) {
"Wrong number of octets in {0}" - f $ipv4_str | Write - Host
return $False
}
foreach($octet in $parts) {
Try {
$oval = [int]::Parse($octet)
if ($oval - le 0 - or $oval - ge 256) {
"Bad octet {0} in IP address {1}" - f $octet, $ipv4_str | Write - Host
return $False
}
} catch {
"Bad octet {0} in IP address {1}" - f $octet, $ipv4_str | Write - Host
return $False
}
}
return $True
}
# sanity check configuration
# - exactly one def route NIC expected
$numDflt = 0
foreach($cfg in $fullConfig) {
if ([String]::IsNullOrEmpty($cfg.netName)) {
Write - Host "Network name is not provided in one of the config entries"
exit
}
if (![String]::IsNullOrEmpty($cfg.gw)) {
$numDflt += 1
if ($cfg.gw - eq "*") {
# DHCP + def GW
if ($cfg.setup - ne "") {
"Network {0}, default non-DHCP interface must provide specific default GW"
`
-f $cfg.NetName | Write-Host
exit
}
} else { # IP address in GW
if ($cfg.setup -eq "") {
"Network {0}, default DHCP interface must pick gw from DHCP options" ` -
f $cfg.NetName | Write - Host
exit
}
}
}
if (![String]::IsNullOrEmpty($cfg.dns)) {
[Char[]] $sep = @(' ', ',')
$servers = $cfg.dns.Split($sep, [System.StringSplitOptions]::RemoveEmptyEntries)
foreach($server in $servers) {
if (!(check_ip($server))) {
"Network {0}, invalid DNS server specified {1}"
`
-f $cfg.NetName, $server | Write-Host
exit
}
}
}
}
if ($numDflt -ne 1) {
"Wrong number of default adapters - {0}, expected 1" -f $numDflt | Write-Host
exit
}
# check vCenter server access credentials
if ([String]::IsNullOrEmpty($srvaddr)) {
$srvaddr = Read-Host -Prompt "Enter vCenter IP or FQDN"
} else {
"Connecting to host {0}" -f $srvaddr | Write-Host
}
if ([String]::IsNullOrEmpty($login)) {
$login = Read-Host -Prompt "Enter vCenter user name"
} else {
"Username {0}" -f $login | Write-Host
}
if ([String]::IsNullOrEmpty($passwd)) {
$passwd = Read-Host -Prompt "Enter password" -AsSecureString
$passwd = $passwd | ConvertFrom-SecureString -AsPlainText
}
$visrv = Connect-VIServer -Server $srvaddr -User $login -Password $passwd
:extloop while ($True) {
# choose VM to edit
do {
$vmlist=Get-VM -Server $visrv | where {$_.Name.StartsWith($drva_prefix)}
Write-Host "Choose VM to reconfigure:"
$idx=0
foreach ($jsvm in $vmlist) {
$idx += 1
"`
t {
0
}: {
1
}
" -f $idx, $jsvm.Name | Write-Host
}
$reply = Read - Host "index, newline to re-read, Q/q to exit "
if ([Char]::ToUpper($reply[0]) - eq 'Q') {
break extloop
}
$reply = $reply - as[int]
}
while ($null - eq $reply - or $reply - le 0 - or $reply - gt $vmlist.Length)
$vm = $vmlist[$reply - 1]
# validate options: all VM networks must be found in config
$nic_list = $vm | Get - NetworkAdapter
foreach($nic in $nic_list) {
$cfg = $fullConfig | Where - Object {
$_.netName - eq $nic.NetworkName
}
if (!$cfg) {
"Network {0} of VM {1} not found in config"
`
-f $nic.NetworkName, $vm.Name | Write-Host
break extloop
}
}
while($True)
{
# validate options: all config networks muts be configured with VM
# print config changes
foreach ($cfg in $fullConfig)
{
$nic = $nic_list | Where-Object { $_.NetworkName -eq $cfg.netName }
if (!$nic)
{
"Network {0} not found on VM {1}" -f $cfg.netName, $vm.Name | Write-Host
break extloop
}
$ip_setup_param = [String]::Format("guestinfo.net.{0}", $nic.MacAddress)
$route_setup_param = [String]::Format("guestinfo.net.routes.{0}", $nic.MacAddress)
$gway_setup_param = [String]::Format("guestinfo.net.gw.{0}", $nic.MacAddress)
$dns_setup_param = [String]::Format("guestinfo.net.dns.{0}", $nic.MacAddress)
$ip_setup = $vm | Get-AdvancedSetting -Name $ip_setup_param
$route_setup = $vm | Get-AdvancedSetting -Name $route_setup_param
$gway_setup = $vm | Get-AdvancedSetting -Name $gway_setup_param
$dns_setup = $vm | Get-AdvancedSetting -Name $dns_setup_param
$msg = ""
if ($ip_setup.Value -ne $cfg.setup)
{
$msg += " config to '{0}';" -f $cfg.setup
}
if ($route_setup.Value -ne $cfg.routes)
{
$msg += " routes to '{0}';" -f $cfg.routes
}
if ($gway_setup.Value -ne $cfg.gw)
{
$msg += " gateway to '{0}';" -f $cfg.gw
}
if ($dns_setup.Value -ne $cfg.dns)
{
$msg += " DNS settings to '{0}';" -f $cfg.dns
}
if (! [String]::IsNullOrEmpty($msg)) {
"`
tChanging {
0
}
on network {
1
}
" -f $msg, $cfg.netName | Write-Host
}
}
$reply = Read - Host "Press enter to continue, Q/q to exit, N/n to choose next VM,"
`
+ " E/e to change IP address"
if ([Char]::ToUpper($reply[0]) -eq 'N')
{
continue extloop
}
if ([Char]::ToUpper($reply[0]) -eq 'Q')
{
break extloop
}
if ([Char]::ToUpper($reply[0]) -eq 'E')
{
foreach ($cfg in $fullConfig)
{
if ([String]::IsNullOrEmpty($cfg.setup)) {
"Interface {0} configured for DHCP" -f $cfg.netName | Write-Host
} else {
do {
$new_ip = Read-Host ("IP for interface {0}" -f $cfg.netName)
} while (! $new_ip -or !(check_ip($new_ip)))
$parts = $cfg.setup.Split('#')
$cfg.setup = "{0}#{1}" -f $new_ip, $parts[1]
}
}
continue
}
break
}
# perform GW config changes
if ($vm.PowerState -ne [VMware.VimAutomation.ViCore.Types.V1.Inventory.PowerState]::PoweredOff) {
Stop-VM -VM $vm -Confirm:$False
}
foreach ($nic in $nic_list) {
$cfg = $fullConfig | Where-Object {$_.netName -eq $nic.NetworkName}
$ip_setup_param = [String]::Format("guestinfo.net.{0}", $nic.MacAddress)
$route_setup_param = [String]::Format("guestinfo.net.routes.{0}", $nic.MacAddress)
$gway_setup_param = [String]::Format("guestinfo.net.gw.{0}", $nic.MacAddress)
$dns_setup_param = [String]::Format("guestinfo.net.dns.{0}", $nic.MacAddress)
$ip_setup = $vm | Get-AdvancedSetting -Name $ip_setup_param
$route_setup = $vm | Get-AdvancedSetting -Name $route_setup_param
$gway_setup = $vm | Get-AdvancedSetting -Name $gway_setup_param
$dns_setup = $vm | Get-AdvancedSetting -Name $dns_setup_param
if ($ip_setup.Value -ne $cfg.setup) {
$ip_setup | Remove-AdvancedSetting -Confirm:$False
if (! [String]::IsNullOrEmpty($cfg.setup)) {
$vm | New-AdvancedSetting -Name $ip_setup_param -Value $cfg.setup -Confirm:$False
}
}
if ($route_setup.Value -ne $cfg.routes) {
$route_setup | Remove-AdvancedSetting -Confirm:$False
if (! [String]::IsNullOrEmpty($cfg.routes)) {
$vm | New-AdvancedSetting -Name $route_setup_param -Value $cfg.routes -Confirm:$False
}
}
if ($gway_setup.Value -ne $cfg.gw) {
$gway_setup | Remove-AdvancedSetting -Confirm:$False
if (! [String]::IsNullOrEmpty($cfg.gw)) {
$vm | New-AdvancedSetting -Name $gway_setup_param -Value $cfg.gw -Confirm:$False
}
}
if ($dns_setup.Value -ne $cfg.dns) {
$dns_setup | Remove-AdvancedSetting -Confirm:$False
if (! [String]::IsNullOrEmpty($cfg.dns)) {
$vm | New-AdvancedSetting -Name $dns_setup_param -Value $cfg.dns -Confirm:$False
}
}
}
Start-VM -VM $vm -Confirm:$False
}
Disconnect-VIServer -Server $global:DefaultVIServers -Confirm:$False