Create Ubuntu 20.04 autoinstall ISO
Step-by-step guide to create the autoinstall ISO for subiquity based Ubuntu Server
Summary
Autoinstallation of an ISO lets us pre-configure the answers to all the configuration questions ahead of time so that the OS installation can happen without user intervention. Ubuntu Server version 18.04 LTS uses the debian-installer (d-i) for the installation process. This includes support for 'preseeding' to create unattended (automated) installations of ubuntu. However with Ubuntu 20.04, the debian installer was replaced by the new 'subiquity server installer'.
This article outlines the steps to follow to create an unattended ISO for Ubuntu 20.04.
Process overview
The three major steps involved in getting an autoinstall ISO for Ubuntu 20.04 are:
- Download the AMD64 live server ISO from official Ubuntu website
- Create a 'user-data' file with all the auto-install parameters in place.
- Modify isolinux/txt.cfg and boot/grub/grub.cfg so that the created ISO will pick the auto-install script during the installation process.
Let's get started with preparing our environment.
Download dependent packages
# Install 7z (optional)
sudo apt-get install p7zip-full
# Install xorriso
sudo apt-get install -y xorriso
# Install isolinux
sudo apt-get install isolinux
Step 1: Download and extract the ISO
The first step is to download the live server ISO from official Ubuntu website releases.ubuntu.com/20.04 eg. releases.ubuntu.com/20.04/ubuntu-20.04.2-li..
After downloading the ISO, we can either choose to extract the ISO using 7z or can mount the ISO and copy the files.
# METHOD 1: USING 7z TO EXTRACT THE ISO
# Download ISO Installer:
wget https://ubuntu.volia.net/ubuntu-releases/20.04.2/ubuntu-20.04.2-live-server-amd64.iso
# Create ISO distribution dirrectory:
mkdir -p iso/nocloud/
# Extract ISO using 7z:
7z x ubuntu-20.04.2-live-server-amd64.iso -x'![BOOT]' -oiso
# METHOD 2: MOUNT THE ISO & COPY THE FILES
# mount the ISO to a local folder
mount -o loop <path to iso> <path to mount it to>
# copy files from mount folder to a new folder
cp -rT <path to mount it to> <new path where modifications are going to happen>
# change folder ownership to root
chown root:root <new path where modifications are going to happen>
Step 2: Create user-data and meta-data files with auto-install data
We will now create a user-data and a meta-data file inside the nocloud folder
# Create empty meta-data file:
touch iso/nocloud/meta-data
# Copy user-data file:
touch iso/nocloud/user-data
When the manual installation is done for Ubuntu 20.04, we can find the cloud-init user-data YAML for this particular configuration in the following file:
/var/log/installer/autoinstall-user-data
We can now use the above file to build the autoinstall script we want to use for our ISO. According to the documentation, this is a minimum viable configuration for the user-data file. We will now add the auto-install data to the 'user-data' file we created in iso/nocloud path:
#cloud-config
autoinstall:
version: 1
identity:
hostname: tpc-e7-08
password: "$6$BzPDXLjjzuA.w21j$STxZ0aB3LbwkBuaPgrdoJBNF1zhFj7duEr0gANKLGVdwGP/wJnDAgPCw1FZGSq9i4GczEt4J4gdsCkdWiH6.Z0"
username: deploy
The password in the sample above is the hash of "password". The above script performs a basic install + apt upgrade of the new system with a disk configuration based on an LVM layout. The complete schema of all the available options for the autoinstall script can be found here: ubuntu.com/server/docs/install/autoinstall-..
The password hash can be calculated the following way in Ubuntu
# generate the password hash
pwhash=$(echo "password" | mkpasswd -s -m sha-512)
We will now go ahead and see how to add more information to the user-data file to add more configurations.
Injecting a public SSH key for the user
ssh:
allow-pw: yes
install-server: true
authorized-keys:
- ssh-rsa <public_key>
allow-pw
: specifies whether to allow SSH using password or not.install-server
: Whether to install OpenSSH server in the target system.authorized-keys
: A list of SSH public keys to install in the initial user’s account
Configure apt during installation
apt:
geoip: true
preserve_sources_list: false
primary:
- arches: [amd64, i386]
uri: http://archive.ubuntu.com/ubuntu
- arches: [default]
uri: http://ports.ubuntu.com/ubuntu-ports
Early commands
A list of shell commands to invoke as soon as the installer starts, in particular before probing for block and network devices
early-commands:
- systemctl stop ssh
Packages to install
packages:
- ntp
- systemd
- curl
- build-essential
Keyboard config
keyboard:
layout: us
toggle: ''
variant: ''
Network configuration
network:
version: 2
renderer: networkd
ethernets:
eno3: {}
eno2:
dhcp4: true
dhcp-identifier: mac
nameservers:
search: <name server search domains>
addresses: <dns IP>
bridges:
admin:
dhcp4: yes
interfaces:
- eno3
br0:
dhcp4: yes
gateway4: xx.xx.xx.xx
interfaces:
- eno2
In this example here, we are configuring ethernet ports and creating bridges as part of the auto-install. Other possible network configuration examples can be seen here: netplan.io/examples
To assign IP statically instead of using dhcp, we can provide the config values like this:
network:
version: 2
renderer: networkd
ethernets:
eno2:
dhcp4: false
addresses:
- yy.yy.yy.yy/24
gateway4: xx.xx.xx.xx
nameservers:
addresses: <DNS IP>
search: <nameserver search domains>
ntp settings
ntp:
enabled: true
ntp_client: chrony
pools: [0.us.pool.ntp.org, 1.us.pool.ntp.org]
servers: [ntp.ubuntu.com]
user-data
This section can be used to pass extra user-data to the autoinstall file
user-data:
disable_root: false
timezone: America/Toronto
package_update: true
package_upgrade: true
late-commands
Shell commands to run after the install has completed successfully and any updates and packages installed, just before the system reboots.
In this example, the umount
statement is unmounting the /cdrom/ before reboot.
late-commands:
- service ntp restart
- echo 'root ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/root
- curtin in-target --target=/target -- echo "Please wait...will reboot automatically"
- curtin in-target --target=/target -- umount -l -r -f /cdrom/
- reboot
Interactive sections
If we want, we can keep some sections whose data is provided by the autoinstall script while we can choose to keep some section interactive
interactive-sections:
- network
- storage
Storage layout
The following config creates root partition with 4GB and doesn't create swap.
storage:
layout:
name: lvm
The following config creates root partition with full space available on disk and also creates swap
storage:
layout:
name: direct
To create a custom layout, we can specify the config by looking at the data in /var/log/installer/autoinstall-user-data
Here's a sample code snippet:
storage:
config:
- grub_device: true
id: disk-sda
path: /dev/sda
ptable: gpt
type: disk
wipe: superblock-recursive
- device: disk-sda
flag: bios_grub
id: partition-0
number: 1
size: 1048576
type: partition
- device: disk-sda
id: partition-1
number: 2
size: -1
type: partition
wipe: superblock
- fstype: ext4
id: format-0
type: format
volume: partition-1
- device: format-0
id: mount-0
path: /
type: mount
The autofill option for disk config can be specified in two ways:
- a negative size can be used for the final partition to indicate that the partition should use all the remaining space. eg.
size: -1
- configure a logical volume to fill a volume group with the property eg.
size: 100%
Step 3: Update the boot flags with cloud-init autoinstall
Once we have the use-data file updated with all the auto-install data, we can go ahead and update grub.cfg and isolinux/txt.cfg to pick the user-data in nocloud folder for autoinstall
# Update boot flags with cloud-init autoinstall:
## Should look similar to this: initrd=/casper/initrd quiet autoinstall ds=nocloud;s=/cdrom/nocloud/ ---
sed -i 's|---|autoinstall ds=nocloud\\\;s=/cdrom/nocloud/ ---|g' iso/boot/grub/grub.cfg
sed -i 's|---|autoinstall ds=nocloud;s=/cdrom/nocloud/ ---|g' iso/isolinux/txt.cfg
Create the ISO
Now that we have the auto-install script and updated the grub with this information, we can now create the ISO
xorriso -as mkisofs -r -V Ubuntu\ custom\ amd64 -o ubuntu-20.04.2-live-server-amd64-autoinstall.iso -J -l -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -isohybrid-apm-hfsplus -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin iso/boot iso
Related articles
- Reference Manual Reference for each particular option of the user-data YAML
- Introduction An overview of the new installer with examples
- Autoinstall quick start Example of booting a VM using the installer using KVM
- github examples Github repo with about twenty examples of more complex configurations
- netplan examples A quick reference for netplan config format for various scenarios
- nocloud Nocloud reference