VMware Firstboot Network Configuration Script For Ubuntu
๐งญ Goal
This script configures static network settings (hostname, IP address, subnet, gateway, DNS) automatically on the first boot of a VMware virtual machine. It pulls values passed via VMwareโs guestinfo properties and applies them using Netplan.
๐ ๏ธ Prerequisites
Requirement | Description |
---|---|
OS | Ubuntu 18.04+ (using Netplan) |
VMware Tools | open-vm-tools or equivalent must be installed |
VMware GuestInfo Settings | Must set guestinfo.labvm.* via vSphere or cloud-init |
๐ฆ Input Parameters via guestinfo
Set the following advanced options in vSphere or through OVF:
Key | Example Value | Description |
---|---|---|
guestinfo.labvm.hostname |
labvm01 |
Hostname for the VM |
guestinfo.labvm.ipaddr |
192.168.1.101 |
Static IP address |
guestinfo.labvm.subnet |
24 |
Subnet mask in CIDR |
guestinfo.labvm.gateway |
192.168.1.1 |
Default gateway |
guestinfo.labvm.dns |
8.8.8.8,1.1.1.1 |
DNS servers (comma-separated) |
๐ง How the Script Works
-
Fetches guestinfo values using
vmtoolsd --cmd info-get ...
-
Validates all inputs (non-empty, valid CIDR for subnet)
-
Detects default interface automatically
-
Sets the hostname using
hostnamectl
-
Generates a Netplan YAML config in
/etc/netplan/01-netcfg.yaml
-
Applies Netplan, validates it, sets file permissions
-
Logs each step to
/var/log/vmware-netconfig.log
-
If everything succeeds:
-
touch /etc/firstboot-done
-
systemctl disable firstboot-network-config.service
-
โ๏ธ Script Path & Permissions
Create a script as:
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 |
#!/bin/bash LOG_FILE="/var/log/vmware-netconfig.log" # Logging function log() { local level="$1" local msg="$2" echo "$(date '+%F %T') [$level] $msg" | tee -a "$LOG_FILE" } log "INFO" "Starting network configuration" # Wait for VMware Tools (vmtoolsd) for i in {1..10}; do if command -v vmtoolsd >/dev/null && vmtoolsd --cmd "info-get guestinfo.ping" &>/dev/null; then log "INFO" "VMware Tools (vmtoolsd) is available" break fi log "INFO" "Waiting for vmtoolsd to become available..." sleep 1 done # Fetch values using vmtoolsd HOSTNAME=$(vmtoolsd --cmd "info-get guestinfo.labvm.hostname") IPADDR=$(vmtoolsd --cmd "info-get guestinfo.labvm.ipaddr") SUBNET=$(vmtoolsd --cmd "info-get guestinfo.labvm.subnet") GATEWAY=$(vmtoolsd --cmd "info-get guestinfo.labvm.gateway") DNS=$(vmtoolsd --cmd "info-get guestinfo.labvm.dns") # Log values log "INFO" "HOSTNAME = $HOSTNAME" log "INFO" "IPADDR = $IPADDR" log "INFO" "SUBNET = $SUBNET" log "INFO" "GATEWAY = $GATEWAY" log "INFO" "DNS = $DNS" # Validate inputs if [[ -z "$HOSTNAME" || -z "$IPADDR" || -z "$SUBNET" || -z "$GATEWAY" || -z "$DNS" ]]; then log "ERROR" "Missing one or more network parameters. Aborting." exit 1 fi # Validate subnet if ! [[ "$SUBNET" =~ ^[0-9]+$ ]] || [ "$SUBNET" -lt 0 ] || [ "$SUBNET" -gt 32 ]; then log "ERROR" "Invalid subnet: $SUBNET. Must be CIDR (e.g., 24)." exit 1 fi # Detect default interface for i in {1..10}; do INTERFACE=$(ip route | awk '/default/ {print $5; exit}') if [ -n "$INTERFACE" ]; then break fi sleep 1 done if [ -z "$INTERFACE" ]; then INTERFACE=$(ip -o link show | awk -F': ' '!/lo/ {print $2; exit}') log "WARNING" "Default route not found. Fallback to: $INTERFACE" fi log "INFO" "Using interface: $INTERFACE" # Set hostname log "INFO" "Setting hostname to $HOSTNAME" if hostnamectl set-hostname "$HOSTNAME"; then grep -q "$HOSTNAME" /etc/hosts || echo "127.0.1.1 $HOSTNAME" >> /etc/hosts else log "ERROR" "Failed to set hostname" exit 1 fi # Write Netplan configuration NETPLAN_FILE="/etc/netplan/01-netcfg.yaml" log "INFO" "Writing Netplan config to $NETPLAN_FILE" cat <<EOF > "$NETPLAN_FILE" network: version: 2 renderer: networkd ethernets: $INTERFACE: dhcp4: no addresses: - ${IPADDR}/${SUBNET} nameservers: addresses: [${DNS//,/\, }] routes: - to: default via: $GATEWAY EOF chmod 600 "$NETPLAN_FILE" log "INFO" "Netplan config file permissions set to 600" # Apply Netplan and check result log "INFO" "Applying Netplan" if netplan apply; then log "INFO" "Netplan applied successfully" else log "ERROR" "Netplan apply failed" exit 1 fi # Only if all succeeded: touch /etc/firstboot-done systemctl disable firstboot-network-config.service log "INFO" "Network configuration completed successfully and service disabled" |
Save the script as:
Set permission to the file
1 |
chmod +x /usr/local/bin/configure_network.sh |
๐ Systemd Service Setup (to run on first boot)
Create file: /etc/systemd/system/firstboot-network-config.service
1 2 3 4 5 6 7 8 9 10 11 12 |
[Unit] Description=First Boot Network Configuration After=network.target ConditionPathExists=!/etc/firstboot-done [Service] ExecStart=/usr/local/bin/configure_network.sh Type=oneshot RemainAfterExit=true [Install] WantedBy=multi-user.target |
Enable the service
1 2 3 |
systemctl daemon-reexec systemctl daemon-reload systemctl enable firstboot-network-config.service |
It will run once during boot, then disable itself.
๐ Log Output Example
All activity is logged to /var/log/vmware-netconfig.log
:
1 2 3 4 5 6 7 8 9 10 11 12 |
2025-04-12 14:30:01 [INFO] Starting network configuration 2025-04-12 14:30:02 [INFO] HOSTNAME = labvm01 2025-04-12 14:30:02 [INFO] IPADDR = 192.168.1.101 2025-04-12 14:30:02 [INFO] SUBNET = 24 2025-04-12 14:30:02 [INFO] GATEWAY = 192.168.1.1 2025-04-12 14:30:02 [INFO] DNS = 8.8.8.8 2025-04-12 14:30:02 [INFO] Using interface: ens33 2025-04-12 14:30:02 [INFO] Writing Netplan config to /etc/netplan/01-netcfg.yaml 2025-04-12 14:30:02 [INFO] Netplan config file permissions set to 600 2025-04-12 14:30:02 [INFO] Applying Netplan 2025-04-12 14:30:03 [INFO] Netplan applied successfully 2025-04-12 14:30:03 [INFO] Network configuration completed successfully and service disabled |
๐งช Troubleshooting
Issue | Solution |
---|---|
๐ด Could not detect default interface |
Ensure network is up and ip route returns default |
โ ๏ธ gateway4 is deprecated |
Already fixed โ script uses routes: |
๐ Permissions for 01-netcfg.yaml are too open |
Script sets chmod 600 |
๐ Netplan apply fails |
Check /etc/netplan/01-netcfg.yaml syntax and try netplan try manually |
You can create an OVF template with this script pre-injected, enabling fully automated VM deployments where network settings and other configurations are applied seamlessly during the initial boot. Note, that during the deployment, the following values need to be updated in VM Advance settings.
You can use the following script to deploy the VM and update the respective values.
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 |
#Connect-VIServer $VMHost = Get-VMHost $DataStore = Get-Datastore -Name "Datastore-SSD-01" $OVF = "D:\Lab\Installer\OVF\Ubuntu-Clean-Build\Ubuntu-Clean-Build.ovf" # Define an array of PSObjects $vmList = @() # Add entries to the array $vmList += [pscustomobject]@{ Hostname = "labvm01" IP = "192.168.1.101" Subnet = "24" Gateway = "192.168.1.1" DNS = "8.8.8.8" } foreach($vmObj in $vmList){ Import-VApp -Name $vmObj.Hostname -Datastore $DataStore -VMHost $VMHost -DiskStorageFormat Thin -Source $OVF -Force $VM = Get-VM $vmObj.Hostname $VM | New-AdvancedSetting -Name "guestinfo.labvm.hostname" -Value $vmObj.Hostname -Confirm:$false $VM | New-AdvancedSetting -Name "guestinfo.labvm.ipaddr" -Value $vmObj.IP -Confirm:$false $VM | New-AdvancedSetting -Name "guestinfo.labvm.gateway" -Value $vmObj.Gateway -Confirm:$false $VM | New-AdvancedSetting -Name "guestinfo.labvm.subnet" -Value $vmObj.Subnet -Confirm:$false $VM | New-AdvancedSetting -Name "guestinfo.labvm.dns" -Value $vmObj.DNS -Confirm:$false $VM | start-vm } |